/* Klödd DS – temalagret. EN fil (ADR 0017).
 Identiteten portad från gamla klodd-ds ljusa tema 2026-07-13: varm cream-yta,
 bläck-text, taupe-ramar, indigo brand-accent sparsamt. shadcn-vokabulär som
 bas + Klödd-utökningar (yt-z-stacken, texthierarkins tredje nivå,
 fält-tokens, tag-palett).

 TVÅ LAGER, EN FIL. :root bär Klödds semantiska tokens; @theme mappar dem till
 Tailwinds namnrymder. Indirektionen är med flit: den bevarar kund-temats
 vitlista (ADR 0010) och shadcn-vokabulären (ADR 0004), som bägge hade fallit
 om värdena skrivits direkt i @theme.

 Fram till 2026-08-20 låg mappningen i en separat klodd-preset.js, och att den
 kunde installeras utan värdena - eller tvärtom - var systemets dyraste
 återkommande bugg (granskningens F1 och F2). I v4 finns ingen andra halva.

 FÄRGERNA ÄR oklch. Konverterade ur HSL via sRGB med exakt rundgång: största
 avvikelse 2.7e-5 av 255 nivåer, alltså identiska efter rundning och inget
 kontrastpar kan ha flyttat sig. Gotchas ärvda: taupe är RAM inte text (faller
 WCAG som brödtext); text-on-accent är LJUS (brand-indigon är mörk). */
:root {
  /* Ytor (cream-ramp, z-stack: overlay ljusast -> active mörkast) */
  --background: oklch(93.448% 0.01405 88.883);          /* warm-page #ede9df */
  --card: oklch(96.447% 0.00984 87.471);           /* warm-card #f6f3ec */
  --popover: oklch(98.475% 0.00658 97.349);        /* warm-overlay #fbfaf5 */
  --muted: oklch(94.106% 0.01281 87.486);          /* warm-section #efebe2 */
  --hover: oklch(91.905% 0.01693 87.506);          /* warm-hover #e9e4d8 */
  --active: oklch(88.895% 0.02131 88.927);         /* warm-active #e0dacb */
  --field: oklch(90.395% 0.01816 88.909);          /* warm-sunken #e4dfd2 - input/sänkt */
  --field-hover: oklch(88.895% 0.02131 88.927);    /* warm-active */

  /* Text (bläck, tre nivåer) */
  --foreground: oklch(21.695% 0.00383 106.715);        /* ink #1a1a18 (~13:1 mot cream) */
  --card-foreground: oklch(21.695% 0.00383 106.715);
  --popover-foreground: oklch(21.695% 0.00383 106.715);
  --muted-foreground: oklch(44.377% 0.01542 87.554);  /* ink-subtle #57534a (~6:1) */
  --faint-foreground: oklch(48.730% 0.01645 88.967);   /* ink-faint #636055 - hint/dämpad, 5.24:1 mot sidan.
                                          Historik: #807b6e (3.48:1) föll AA helt; #6b675c (39.1%)
                                          klarade de fyra ljusaste ytorna men föll mot --hover
                                          (4.44), --field (4.24) och --active (4.04). Doktrinen
                                          påstod ändå "alla tre klarar AA mot hela z-stacken", och
                                          vakten mätte bara nivå 3 mot fyra av sju ytor - alltså
                                          exakt de ytor där den klarade sig.
                                          Nu klarar den ALLA sju, och vakten mäter alla sju.
                                          Priset är en tätare hierarki (6.30 / 5.24 mot sidan i
                                          stället för 6.30 / 4.65). Det är rätt pris: en regel med
                                          tre undantag är inte en regel man minns. */

  /* Ramar (taupe) */
  --border: oklch(88.526% 0.01829 88.912);         /* taupe-subtle #ded9cc */
  --border-strong: oklch(71.820% 0.01761 91.606);    /* taupe #a8a498 - EJ text */
  --input: oklch(88.526% 0.01829 88.912);

  /* Brand (indigo, sparsam) */
  --primary: oklch(43.844% 0.17611 279.238);         /* accent-brand #433caf */
  --primary-foreground: oklch(98.475% 0.00658 97.349);  /* text-on-accent = LJUS */
  --primary-hover: oklch(38.969% 0.15603 279.283);   /* accent-brand-hover #393295 */
  --ring: oklch(43.844% 0.17611 279.238);
  --secondary: oklch(96.447% 0.00984 87.471);       /* warm-card */
  --secondary-foreground: oklch(21.695% 0.00383 106.715);

  /* Status (destruktivt). Samma split som tag-paletten: en SOLID för ytor
     och ramar, en TEXT-ton för destruktiv text på cream. Radix red-9 klarade
     ingendera (ljus text 3.75:1, som text 3.23:1, som ram 2.94:1) - stegen
     nedan är omkalibrerade mot cream och bevakas av scripts/check-contrast.mjs. */
  --destructive: oklch(55.689% 0.19733 25.052);          /* #ce2c31 red-light-11 - ljus text 4.99:1 */
  --destructive-hover: oklch(49.781% 0.17465 24.871);    /* #b1262a - ljus text 6.35:1 */
  --destructive-foreground: oklch(98.475% 0.00658 97.349);
  --destructive-text: oklch(50.228% 0.17669 24.919);     /* #b3262b - destruktiv TEXT på cream, 5.36:1 */

  --radius: 0.5rem;

  /* Klödds röst (ADR 0014, valet fattat 2026-08-19): Protest Strike.
     Vald mot EB Garamond och Caprasimo på renderingar av en gemensam
     e-handelsyta, inte på isolerade rubriker. Bredden fällde Caprasimo
     (28 % bredare vid samma punktstorlek - på svenska, där
     "e-handelsplattform" är ett normalord, blir det en återkommande
     begränsning). Registerskillnaden fällde Garamond: mot Inter som
     brukstext ger Protest Strike en ENTYDIG arbetsfördelning, medan
     Garamond mot Inter är en gradskillnad.
     Konsumenter måste själva hosta woff2:orna - registryt kan inte
     leverera binärer. Se docs/distribution.md. */
  --font-voice: "Protest Strike", "Impact", "Arial Narrow", sans-serif;

  /* Spärr för rösten. Snittbunden OCH storleksberoende. Protest Strike är
     redan tät - Garamonds -0.022em kvävde den. Värdena nedan är satta för
     Protest Strike och ska räknas om om rösten någonsin byts. */
  --voice-tracking-heading: -0.005em;
  --voice-tracking-display: -0.01em;

  /* Röstens vikt är en TOKEN, aldrig ett hårdkodat font-medium. EB Garamond
     är laddad i 500, men kandidatsnitten (Caprasimo, Protest Strike) finns
     bara i 400. Bad en komponent om 500 skulle browsern syntetisera fetstil
     ur 400-ansiktet och röstprovet jämföra äpplen med falska päron. */
  --voice-weight: 400;

  /* Radavståndsgolv, SNITTBUNDET. Uppmätt bläckutbredning normaliserad
     till 100px em: EB Garamond 500 spänner 114 (Å-ringens topp 85 plus
     underhäng 29), Protest Strike 109, Caprasimo 109. Garamonds bläck är
     alltså 6 % högre än en radbox på 1.08 rymmer, så ett Å på rad två kan
     skära in i rad ettans underhäng. Det kräver att ett underhäng råkar
     sitta rakt ovanför ett Å - men ett system ska inte vila på horisontell
     tur.

     VIKTIGT: golvet är INTE bara kollisionströskeln. Bläckutbredningen
     säger var raderna börjar krocka geometriskt; perceptuell trängsel är
     bindande långt innan dess på ett mörkt snitt. Protest Strike klarar
     1.10 geometriskt men klibbar ihop visuellt - 1.20 är designvärdet.
     Kollisionströskel och designinställning är två olika tal. */
  --voice-leading-floor: 1.2em;

  /* DISPLAY-RAMPEN (ADR 0014). Egen, glesare ramp som börjar där
     utility-rampen slutar. Fem steg, kvot ~1.29, fluid mellan 375 och
     1440px. Golvet i varje steg ligger ÖVER utility-toppen (30px) så att
     ramperna aldrig korsar varandra vid mellanbredder - det är den
     vanligaste buggen i fluida skalor.
     Utility-rampen är med flit FAST: 14px brödtext får inte krympa på
     mobil och ska inte växa på desktop, täthet är poängen där.
                       375px   1440px
       display-sm       32  ->    42
       display-md       36  ->    54
       display-lg       40  ->    70
       display-xl       46  ->    90 (--display-top)

     FYRA steg, inte fem. Rampen ritades med ett femte steg som slutade på
     112px, och när röstvalet sänkte taket till 90 räknades bara det steget
     om - medan display-xl redan hade 5.625rem (90px) som eget tak. Från
     1440px och uppåt var de två översta stegen alltså EXAKT lika stora, och
     ADR 0014:s stickprov vid åtta bredder råkade inte fånga det.

     Fyra steg är dessutom en jämnare ramp: kvoterna blir 1.286 / 1.296 /
     1.286, medan det borttagna steget hade 1.244 och redan var det som
     avvek. Toppsteget heter nu display-xl. scripts/check-ramp.mjs räknar
     varje steg vid varje bredd 320-2560 så att det inte kan hända igen. */
  --display-top: 5.625rem; /* 90px. 112 blev för mycket för ett så mörkt
                              snitt; satt med öga på renderingarna. */
  --display-sm: clamp(2rem, 1.7799rem + 0.939vw, 2.625rem);
  --display-md: clamp(2.25rem, 1.8539rem + 1.6901vw, 3.375rem);
  --display-lg: clamp(2.5rem, 1.8398rem + 2.8169vw, 4.375rem);
  --display-xl: clamp(2.875rem, 1.9067rem + 4.1315vw, var(--display-top));

  /* Skuggor (ink-baserade, ADR 0013 light-doktrin) */
  --shadow-card: 0 1px 3px oklch(21.695% 0.00383 106.715 / 0.12), 0 1px 2px oklch(21.695% 0.00383 106.715 / 0.08);
  --shadow-float: 0 4px 12px oklch(21.695% 0.00383 106.715 / 0.16), 0 2px 4px oklch(21.695% 0.00383 106.715 / 0.10);

  /* Tag-palett (Radix light 9 via color-mix - enhetlig formel:
     bg = 9 @ 12% över ytan, border = 9 @ 30%). TEXT-stegen är Radix 11
     NEDMÖRKADE för cream: Radix 11 är ritad för vit botten och föll till
     3.8-4.9:1 över våra ytor. Nu >= 4.65:1 mot soft-ytan över --background,
     bevakat av scripts/check-contrast.mjs. */
  --tag-grey-solid: oklch(64.602% 0.01927 292.918);   --tag-grey-text: oklch(49.083% 0.01595 294.978);
  --tag-green-solid: oklch(64.060% 0.13288 157.680);  --tag-green-text: oklch(47.683% 0.09694 159.655);
  --tag-red-solid: oklch(62.557% 0.19334 23.026);    --tag-red-text: oklch(50.271% 0.17678 24.815);
  --tag-blue-solid: oklch(64.929% 0.19304 251.780);   --tag-blue-text: oklch(48.568% 0.14016 252.062);
  --tag-orange-solid: oklch(85.370% 0.15721 84.130); --tag-orange-text: oklch(51.348% 0.11606 64.082);
  --tag-purple-solid: oklch(57.874% 0.18772 322.107); --tag-purple-text: oklch(50.170% 0.16445 321.976);

  /* Rörelse (ADR 0011). Fyra durationer, två easings - hela systemets
     rörelsevokabulär. Tailwind-bryggan sätter dessa som DEFAULT för
     transition-* och animate-in/out, så även otokeniserad rörelse går
     genom dem. Reduced motion skriver om dem längst ner i filen. */
  --duration-instant: 100ms;  /* mikroåterkoppling: ikonrotation, pressed */
  --duration-fast: 150ms;     /* IN: hover, fokus, färgskiften, overlays */
  --duration-normal: 250ms;   /* UT: hover av, overlays ut, panelbyten */
  --duration-slow: 400ms;     /* geometri: sheet/drawer, bildskala */

  --ease-out: cubic-bezier(0.19, 1, 0.22, 1);          /* allt som kommer in */
  --ease-in-out: cubic-bezier(0.645, 0.045, 0.355, 1); /* allt som flyttar sig */

  /* GATADE RÖRELSETOKENS (ADR 0015). De finns ALLTID, men med app-säkra
     värden. Ett tonläge höjer dem; utility-läget lämnar dem som de är.
     Det gör att en komponent kan deklarera fjädrande geometri en gång och
     ändå snäppa i en priskonfigurator - 0ms är inte "avstängt", det är
     tillståndsbytet direkt. Ingen komponent behöver veta vilket ton den
     står i. */
  --duration-slower: 0ms;                   /* brand-dynamisk: 600ms */
  /* Returtiden för fjädrande geometri. Asymmetrisk med flit, och tvärtom
     mot ADR 0011 princip 2: fjädern är det som ska hinna läsas på vägen
     IN (600ms), medan vägen UT bara ska komma undan (250ms). En lång,
     fjädrande retur läses som att knappen inte släpper taget. */
  --duration-slower-ut: var(--duration-slower);
  --duration-ambient: var(--duration-slow);  /* brand-dynamisk: 1200ms */
  --delay-ambient: 0ms;                     /* brand-dynamisk: 400ms */
  --ease-spring: var(--ease-out);           /* brand-dynamisk: fjädern */
  --entre-forflyttning: 16px;               /* brand-dynamisk: 32px */

  /* Knappens CTA-mekanik (ADR 0015). --knapp-ikon är STRUKTUREN: pilens
     bredd, och samtidigt det tal paddingen växer med på varje sida. Att
     skriva calc(var(--knapp-ikon) + ...) i stället för calc(16px + ...)
     är skillnaden mellan ett strukturellt tal och ett magiskt.
     Tillväxten är ASYMMETRISK med flit. Förlagan växte symmetriskt, med
     motiveringen att etiketten är centrerad - vilket stämmer när ikonen har
     KONSTANT bredd. Med en pil som växer ur noll gör det inte det: uppmätt
     flyttade etiketten 16px och drev 8px ur centrum. Knappen är shrink-to-fit
     och vänsterkanten är låst, alltså är etikettens position exakt lika med
     vänsterpaddingen. Bara högersidan växer. Se ADR 0015. */
  --knapp-ikon: 1rem;
  --knapp-pil-vilo: var(--knapp-ikon); /* brand-dynamisk: 0px */
  --knapp-tryck: 0px;                  /* brand-dynamisk: 3px */
  --mask-start: 0%;                         /* brand-dynamisk: 100% */
  --ambient-loop: paused;                   /* brand-dynamisk: running */
  --nav-auto-dolj: 0;                       /* brand-dynamisk: 1 */
}

@theme {
  /* Färger. Värdena bor i :root ovan; här får de sina namnrymder. */
  --color-border: var(--border);
  --color-card: var(--card);
  --color-card-foreground: var(--card-foreground);
  --color-popover: var(--popover);
  --color-popover-foreground: var(--popover-foreground);
  --color-hover: var(--hover);
  --color-active: var(--active);
  --color-tag-grey-soft: color-mix(in srgb, var(--tag-grey-solid) 12%, transparent);
  --color-tag-grey-border: color-mix(in srgb, var(--tag-grey-solid) 30%, transparent);
  --color-tag-grey-text: var(--tag-grey-text);
  --color-tag-grey-solid: var(--tag-grey-solid);
  --color-tag-green-soft: color-mix(in srgb, var(--tag-green-solid) 12%, transparent);
  --color-tag-green-border: color-mix(in srgb, var(--tag-green-solid) 30%, transparent);
  --color-tag-green-text: var(--tag-green-text);
  --color-tag-green-solid: var(--tag-green-solid);
  --color-tag-red-soft: color-mix(in srgb, var(--tag-red-solid) 12%, transparent);
  --color-tag-red-border: color-mix(in srgb, var(--tag-red-solid) 30%, transparent);
  --color-tag-red-text: var(--tag-red-text);
  --color-tag-red-solid: var(--tag-red-solid);
  --color-tag-blue-soft: color-mix(in srgb, var(--tag-blue-solid) 12%, transparent);
  --color-tag-blue-border: color-mix(in srgb, var(--tag-blue-solid) 30%, transparent);
  --color-tag-blue-text: var(--tag-blue-text);
  --color-tag-blue-solid: var(--tag-blue-solid);
  --color-tag-orange-soft: color-mix(in srgb, var(--tag-orange-solid) 12%, transparent);
  --color-tag-orange-border: color-mix(in srgb, var(--tag-orange-solid) 30%, transparent);
  --color-tag-orange-text: var(--tag-orange-text);
  --color-tag-orange-solid: var(--tag-orange-solid);
  --color-tag-purple-soft: color-mix(in srgb, var(--tag-purple-solid) 12%, transparent);
  --color-tag-purple-border: color-mix(in srgb, var(--tag-purple-solid) 30%, transparent);
  --color-tag-purple-text: var(--tag-purple-text);
  --color-tag-purple-solid: var(--tag-purple-solid);
  --color-border-strong: var(--border-strong);
  --color-faint-foreground: var(--faint-foreground);
  --color-field: var(--field);
  --color-field-hover: var(--field-hover);
  --color-input: var(--input);
  --color-ring: var(--ring);
  --color-background: var(--background);
  --color-foreground: var(--foreground);
  --color-primary: var(--primary);
  --color-primary-foreground: var(--primary-foreground);
  --color-primary-hover: var(--primary-hover);
  --color-secondary: var(--secondary);
  --color-secondary-foreground: var(--secondary-foreground);
  --color-destructive: var(--destructive);
  --color-destructive-foreground: var(--destructive-foreground);
  --color-destructive-hover: var(--destructive-hover);
  --color-destructive-text: var(--destructive-text);
  --color-muted: var(--muted);
  --color-muted-foreground: var(--muted-foreground);

  /* Skuggor, radier, spärr */
  --shadow-card: var(--shadow-card);
  --shadow-float: var(--shadow-float);
  --radius-lg: var(--radius);
  --radius-md: calc(var(--radius) - 2px);
  --radius-sm: calc(var(--radius) - 4px);
  --tracking-voice: var(--voice-tracking-heading);
  --tracking-voice-display: var(--voice-tracking-display);

  /* Typskalan. Bara de steg Tailwind saknar plus display-rampen (ADR 0016). */
  --text-2xs: 0.625rem;
  --text-2xs--line-height: 1rem;
  --text-small: 0.8125rem;
  --text-small--line-height: 1.25rem;
  --text-display-sm: var(--display-sm);
  --text-display-sm--line-height: var(--voice-leading-floor);
  --text-display-md: var(--display-md);
  --text-display-md--line-height: var(--voice-leading-floor);
  --text-display-lg: var(--display-lg);
  --text-display-lg--line-height: var(--voice-leading-floor);
  --text-display-xl: var(--display-xl);
  --text-display-xl--line-height: var(--voice-leading-floor);

  /* Rörelse. Kurvorna har en namnrymd i v4; durationerna har det inte
     och blir @utility nedan. DEFAULT gör att otokeniserad rörelse ändå
     går genom tokensen (ADR 0011). */
  --default-transition-timing-function: var(--ease-out);
  --ease-out: var(--ease-out);
  --ease-in-out: var(--ease-in-out);
  --ease-spring: var(--ease-spring);
  --default-transition-duration: var(--duration-fast);
}

/* Durationerna. v4 har en namnrymd för kurvor (--ease-*) men INGEN för
   durationer, alltså blir de utilities.

   VARJE ÄR TVÅ RADER, och den andra är inte kosmetisk. `transition-duration`
   styr transitions; `--tw-duration` är variabeln tailwindcss-animates
   efterföljare tw-animate-css läser för animate-in/animate-out:

     .animate-in { animation: enter var(--tw-animation-duration,
                                       var(--tw-duration, .15s)) ... }

   I v3 satte presetten `transitionDuration` och animate-pluginen ÄRVDE
   `animationDuration` därifrån - ADR 0011 punkt 3 bygger på det arvet. v4 har
   inget sådant arv, och den första porteringen satte bara transition-raden.
   Följden var tyst och allvarlig: varje data-[state=open]:duration-* på
   Dialog, Sheet, Select, DropdownMenu och Tooltip var död, alla spelade på
   tw-animate-css hårdkodade 150 ms, och reduced motion-blocket - som skriver
   om --duration-* - nådde dem aldrig. En sheet på 75 % av viewportbredden
   gled alltså in i full hastighet för någon som bett systemet låta bli.
   Hittat av en oberoende granskare i den byggda CSS:en, inte av en vakt. */
@utility duration-instant {
  transition-duration: var(--duration-instant);
  --tw-duration: var(--duration-instant);
}
@utility duration-fast {
  transition-duration: var(--duration-fast);
  --tw-duration: var(--duration-fast);
}
@utility duration-normal {
  transition-duration: var(--duration-normal);
  --tw-duration: var(--duration-normal);
}
@utility duration-slow {
  transition-duration: var(--duration-slow);
  --tw-duration: var(--duration-slow);
}
@utility duration-slower {
  transition-duration: var(--duration-slower);
  --tw-duration: var(--duration-slower);
}
@utility duration-ambient {
  transition-duration: var(--duration-ambient);
  --tw-duration: var(--duration-ambient);
}
@utility delay-ambient {
  transition-delay: var(--delay-ambient);
  --tw-animation-delay: var(--delay-ambient);
}

/* Basen. Låg tidigare hos konsumenten, som var tvungen att veta om det -
   och tokenfilen levererade värdena utan att sätta dem, så en konsument som
   glömde steget fick en vit sida. Nu bär itemet sin egen grund. */
@layer base {
  /* v4 låter bar `border` bli currentColor. Klödds ram är taupe
     (DESIGN-LANGUAGE §6), alltså sätts den som default här. */
  *,
  ::after,
  ::before {
    border-color: var(--border);
  }

  /* Fokusringens offset. Tailwinds default är hårdkodat #fff, alltså ritade
     varje fokuserad knapp en 2px REN VIT halo på en sida där ingen yta är vit
     - ljusaste är --popover. Ett hårdkodat värde utanför tokenlagret, mitt i
     fokusmarkeringen, som ingen vakt kände till.
     Med sidytan här blir dessutom kontraktets befintliga par sant: ringen
     mäts mot --background, och det är nu också det den faktiskt ligger på. */
  *,
  ::after,
  ::before {
    --tw-ring-offset-color: var(--background);
  }

  body {
    background-color: var(--background);
    color: var(--foreground);
    font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
      "Helvetica Neue", Ubuntu, sans-serif;
  }
}


/* TON (ADR 0015) - uttrycksläge, satt med data-ton på ETT UNDERTRÄD, inte
   på <html>. En PDP har brand-hero och köpyta på samma sida, och en
   utility-ö inne i en brand-sektion måste få tillbaka basvärdena. Därför
   deklarerar [data-ton="app"] om ALLT som något tonläge kan ha höjt.
   Default är app: glömmer någon attributet blir resultatet stramt i
   stället för stökigt, och det är den billiga riktningen att fela åt.

   Ton styr FÄRG, RÖRELSE och ACCENTFREKVENS. Aldrig typografi.
   Display-rampen är egna klasser som brand-ytor använder eller låter bli
   (ADR 0014, DESIGN-LANGUAGE §8b) - annars skulle den som sveper en
   sektion för lugnare rörelse också få tre gånger större rubriker. */

[data-ton="app"] {
  --duration-slower: 0ms;
  --duration-slower-ut: 0ms;
  --duration-ambient: var(--duration-slow);
  --delay-ambient: 0ms;
  --ease-spring: var(--ease-out);
  --entre-forflyttning: 16px;
  --mask-start: 0%;
  --ambient-loop: paused;
  --nav-auto-dolj: 0;
  --knapp-pil-vilo: var(--knapp-ikon);
  --knapp-tryck: 0px;
}

/* brand-stram: ADR 0011-disciplinen oförändrad. Enda tillägget är
   entrémönstret, som är en klass och inte en token. Att profilen inte
   höjer en enda token är dess hela poäng. */
[data-ton="brand-stram"] {
  --duration-slower: 0ms;
  --duration-slower-ut: 0ms;
  --duration-ambient: var(--duration-slow);
  --delay-ambient: 0ms;
  --ease-spring: var(--ease-out);
  --entre-forflyttning: 16px;
  --mask-start: 0%;
  --ambient-loop: paused;
  --nav-auto-dolj: 0;
  --knapp-pil-vilo: var(--knapp-ikon);
  --knapp-tryck: 0px;
}

/* brand-dynamisk: de två dokumenterade undantagen från 400ms-taket.
   De är RÄCKVIDDSBEGRÄNSADE, inte avsteg - att de bor i ett tonläge och
   inte i en komponent är hela skillnaden mellan ett system och en regel
   som slutat gälla. */
[data-ton="brand-dynamisk"] {
  --duration-slower: 600ms;
  --duration-slower-ut: 250ms;
  --duration-ambient: 1200ms;
  --delay-ambient: 400ms;
  --entre-forflyttning: 32px;
  --mask-start: 100%;
  --ambient-loop: running;
  --nav-auto-dolj: 1;
  --knapp-pil-vilo: 0px;
  --knapp-tryck: 3px;
  /* Egengenererad ur en dämpad harmonisk oscillator (zeta 0.55, wn 9):
     12.6% översläng, en enda svängning, klar vid t=1. Fjädrande geometri
     känns fysisk; fjädrande FÄRG ser fel ut och får aldrig den här. */
  --ease-spring: linear(
    0, 0.0609, 0.2081, 0.3958, 0.5888, 0.7635, 0.906, 1.0111, 1.0794,
    1.1155, 1.1263, 1.1191, 1.1007, 1.0771, 1.0527, 1.0305, 1.0124,
    0.9989, 0.9902, 0.9855, 0.984, 0.9849, 0.9872, 0.9902, 1
  );
}

/* INGEN DARK MODE. Klödd är light-first (ADR 0003), och blocket som låg här
   var en shadcn-slate-platshållare: 13 definierade tokens medan 27 färgtokens
   föll igenom till de LJUSA värdena. En .dark-yta hade ritat kort, overlays,
   inputs och hela tag-paletten i cream på en nästan svart bakgrund.
   Kontraktsvakten hade till och med en egen statusflagga som förklarade att
   läget var trasigt - alltså ärrvävnad ovanpå ärrvävnad.
   Ett läge som är dokumenterat trasigt är sämre än inget läge: det inbjuder
   till att slås på. Borttaget 2026-08-20. Ska dark finnas ritas den av ett
   ADR, från cream-identiteten och inte från slate. check-doktrin.mjs failar
   om ett .dark-block eller en dark:-variant kryper tillbaka. */

/* ==========================================================================
   UTILITIES OCH KEYFRAMES

   Ren CSS, OLAGRAD med flit. I v4 ligger Tailwinds utilities i
   @layer utilities, och olagrad CSS vinner alltid över lagrad - oavsett
   källordning. Det gör den felklass som lagades 2026-08-20 strukturellt
   omöjlig: en hover:duration-fast kan inte längre kollapsa
   .transition-knapps sjuvärdiga lista. I v3 avgjordes det av vem som råkade
   stå sist i filen.
   ========================================================================== */

/* --pil och --tryck är REGISTRERADE med @property, inte vanliga
   custom properties. Skälet är en verklig bugg (granskning 2026-08-20,
   F7): en oregistrerad custom property är en otypad token-ström, och
   Chrome räknar inte om en calc() som beror på den när den ändras, om
   målegenskapen har en aktiv transition. CTA-knappen fastnade därför på
   sitt hover-värde i brand-dynamisk (600ms transition) medan den var
   korrekt i app och brand-stram (0ms, ingen transition att fastna i).

   Registrerade som <length> blir de interpolerbara. Då transitionar VI
   dem, och padding följer med per bildruta i stället för att behöva
   räknas om. Padding ligger därför inte längre i transition-knapp. */
@property --pil {
  syntax: "<length>";
  inherits: true;
  initial-value: 0px;
}
@property --tryck {
  syntax: "<length>";
  inherits: true;
  initial-value: 0px;
}

/* Rösten. EN klass som bär BÅDE familj och vikt - dela dem och
   tailwind-merge äter den ena (den grupperar okända font-* som
   font-family). Vikten måste följa med: kandidatsnitten var laddade i 400
   och hade fått syntetisk fetstil av en vikt som inte hängde med.

   SELEKTORN ÄR DUBBLAD MED FLIT. En enkel .font-voice har samma specificitet
   som vilken utility som helst, och utilities genereras EFTER den här filen.
   Alltså vann varje storleksklass som bär vikt. Starter-skalans -semi-steg
   gör det: `text-2xl-semi` sätter font-weight 600, och NotisPanels rubrik
   renderade därför Protest Strike i 600 - alltså syntetisk fetstil på ett
   snitt som bara finns i 400. Det är ordagrant det --voice-weight infördes
   för att hindra, och det stod i produktion.

   Att laga anropsstället hade lagat ETT anropsställe. Vikten är en invariant
   av snittet och inte en smaksak, alltså ska den vara strukturellt omöjlig
   att skriva över. Två klasser i selektorn räcker; !important behövs inte
   och vore trubbigare. */
.font-voice.font-voice {
  font-family: var(--font-voice);
  font-weight: var(--voice-weight);
}

/* Knappens transition. Olika duration per egenskap, alltså inte
   uttryckbar som atomära utilities. Basen bär UT-durationen. */
.transition-knapp {
  transition-property: color, background-color, border-color, box-shadow,
    transform, --pil, --tryck;
  transition-duration: var(--duration-normal), var(--duration-normal),
    var(--duration-normal), var(--duration-normal), var(--duration-instant),
    var(--duration-slower-ut), var(--duration-slower-ut);
  /* UT-vägen går på ease-out, inte på fjädern. En fjäder har översläng,
     och på vägen TILLBAKA blir överslängen en undersläng: --pil skjuter
     förbi noll till cirka -2px, knappen krymper smalare än sitt viloläge
     och studsar tillbaka. Det läses som en glitch, inte som fysik.
     Fjädrar hör hemma på ankomsten. IN-vägen sätter --ease-spring i
     .knapp-brand:hover nedan. */
  transition-timing-function: var(--ease-out), var(--ease-out),
    var(--ease-out), var(--ease-out), var(--ease-out), var(--ease-out),
    var(--ease-out);
}

/* IN-vägen. Ligger HÄR och inte som `hover:duration-fast` i knappens
   basklass, och det är inte en stilfråga.

   `hover:duration-fast` sätter ETT värde på transition-duration. I v3
   genererades utilityn efter den här filen med samma specificitet som
   `.knapp-brand:hover`, så källordningen avgjorde - utilityn vann, hela
   sjuvärdeslistan kollapsade till 150 ms, och fjädern på 600 ms körde på 150.

   I v4 kan det inte hända: utilities ligger i `@layer utilities` och den här
   filen är olagrad, alltså vinner den alltid. Regeln står kvar ändå. Att en
   olycka blivit omöjlig är inget skäl att skriva om koden så att den bara
   fungerar av en kaskadegenskap man inte ser i filen.

   Det gick ett dygn utan att märkas, eftersom mätningen gjordes via
   `data-primed` - som INTE triggar `:hover` och därför läste 600. Mätvägen
   och pekarvägen var två olika kodvägar. Därför står `[data-primed]` med i
   samma selektor nedan: det som mäts måste vara det som händer.

   För `transition-colors` är `hover:duration-fast` fortfarande rätt - där
   finns bara en egenskap att sätta duration på. Fällan gäller bara den som
   deklarerar en LISTA. */
.transition-knapp:hover,
.transition-knapp[data-primed="true"] {
  transition-duration: var(--duration-fast), var(--duration-fast),
    var(--duration-fast), var(--duration-fast), var(--duration-instant),
    var(--duration-slower), var(--duration-slower);
}

/* CTA-knappen (ADR 0015, viloläget omgjort 2026-08-20).

   TVÅ FÄRGLAGER, EN KLIPPNING. Indigon ligger målad i border-boxen; cremen
   målas ovanpå och klipps till padding-box, alltså 2px innanför kanten. Det
   som blir kvar är ringen. Hover byter inte klippning utan FYLLNING: cremen
   försvinner, hela ytan blir indigo, texten blir ljus.

   Före det här var klippningen `border-box` i vila, alltså cream ända ut, och
   indigon helt övermålad. Knappen låg på 1,09:1 mot sidan - systemets primära
   handling gick inte att lokalisera - och ringen avtäcktes först på hover.
   Hover finns inte på pekskärm, inte i en skärmdump och inte i en jurygenomgång.
   Värre: avtäckningen var gatad till brand-dynamisk, så i app och brand-stram
   gjorde hover ingenting alls. Hittat av två oberoende granskare samma dag,
   från motsatta håll.

   Ringen i vila är 6,90:1 mot sidan; ljus text på fylld indigo är 4,99:1, och
   det paret räknas redan av kontraktsvakten. Ingen ny form, ingen ny token.

   `--knapp-klipp-hover` behövs inte längre och är borta ur ton-kontraktet:
   klippningen är densamma i alla lägen, det är fyllningen som byter.
   Paddingen sitter INTE här utan som pl-/pr-klasser i Buttons
   compoundVariants: padding-inline förlorar mot storleksvariantens physical
   padding-left/right oavsett kaskadordning. */
.knapp-brand {
  --pil: var(--knapp-pil-vilo);
  --tryck: 0px;
  /* --knapp-padding sätts INTE här. Den kommer från storleksvarianten i
     Button, som en arbiträr egenskap i @layer utilities - och den här filen
     är olagrad, alltså hade ett värde här vunnit över alla fyra storlekarna
     och gett dem samma sidopadding. Precis det hände i v4-migreringen, och
     den renderade sviten fångade det. Defaulten bor i fallbacken där
     paddingen faktiskt läses, så asChild och okända storlekar täcks ändå. */
  border: 2px solid transparent;
  background: linear-gradient(var(--card), var(--card)) padding-box,
    var(--primary) border-box;
  color: var(--foreground);
}
.knapp-brand:hover,
.knapp-brand[data-primed="true"] {
  --pil: var(--knapp-ikon);
  /* Durationerna kommer från .transition-knapp:hover ovan. Bara kurvan är
     brand-specifik: fjädern hör hemma på ankomsten. */
  transition-timing-function: var(--ease-out), var(--ease-out),
    var(--ease-out), var(--ease-out), var(--ease-out), var(--ease-spring),
    var(--ease-spring);
  background: var(--primary) border-box;
  color: var(--primary-foreground);
}
/* Den TYSTA CTA:n - andraknappen i ett CTA-par (Calle, 2026-08-20).

   Samma två färglager, samma pil, samma fjäder. Det som skiljer är VILKEN
   av de två sakerna hover flyttar:

     tyst      cremen når hela vägen ut  ->  cremen dras in, ringen tänds
     primär    cremen är indragen, ring  ->  cremen försvinner, ytan fylls

   Alltså en stege med EN accent och tre steg: ingen ring, ring, fylld. Paret
   läses som en indigo-markerad knapp bredvid en crem, och hover flyttar bägge
   ett steg upp på samma stege.

   Den tysta är avsiktligt svagast i vila: 1,09:1 mot sidan, alltså ingen
   kant alls. Det som bär den som knapp är `shadow-card` och etikettens bläck
   (12,7:1) - samma affordans som systemets `secondary` vilar på. Den hör
   därför hemma BREDVID en primär CTA, aldrig ensam på en yta.

   Doubled selector med flit: `.knapp-brand:hover` sätter ljus text och fylld
   yta, och den tysta bär BÅDA klasserna för att ärva pil, padding och
   fjäder. Utan den dubblade selektorn avgör källordning, och det är precis
   den sortens tysta beroende som repot har betalat för förut. */
.knapp-brand-tyst.knapp-brand-tyst {
  background: linear-gradient(var(--card), var(--card)) border-box,
    var(--primary) border-box;
}
.knapp-brand-tyst.knapp-brand-tyst:hover,
.knapp-brand-tyst.knapp-brand-tyst[data-primed="true"] {
  background: linear-gradient(var(--card), var(--card)) padding-box,
    var(--primary) border-box;
  color: var(--foreground);
}

.knapp-brand:active {
  --tryck: var(--knapp-tryck);
}
/* Ingen egen transition här: --pil interpolerar själv (se @property
   ovan), och width följer med per bildruta. */
.knapp-brand [data-pil] {
  width: var(--pil);
  overflow: hidden;
  flex: none;
}

/* Understrykning som ritas från vänster. background-size och inte
   text-decoration, som snäpper. */
.lank-understruken {
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0% 1px;
  transition: background-size var(--duration-normal) var(--ease-out);
}
.lank-understruken:hover,
.lank-understruken:focus-visible {
  background-size: 100% 1px;
}

/* Ambient-väggen. Masken sveper ner, innehållet TELEPORTERAR exakt när det
   är dolt, masken sveper vidare. Innehållscykeln är dubbelt så lång som
   maskens, så att återgången också sker bakom masken. */
@keyframes mask-svep {
  0%,
  35% {
    transform: translateY(-101%);
  }
  45%,
  55% {
    transform: translateY(0);
  }
  65%,
  100% {
    transform: translateY(101%);
  }
}
@keyframes innehall-hopp {
  0%,
  25% {
    transform: translateY(0);
  }
  25.5%,
  75% {
    transform: translateY(-50%);
  }
  75.5%,
  100% {
    transform: translateY(0);
  }
}

/* Reduced motion är ett LÄGE, inte en degradering (ADR 0011): rörelsen
   ersätts av direkt tillståndsbyte, inte av ingenting. Eftersom all duration
   i systemet går genom tokens räcker det att skriva om dem - ingen
   *-selektor med !important behövs.
   Loopar hanteras i komponentkällan med motion-reduce:-varianten:
   skeleton stannar (formen bär betydelsen), knappens spinner behålls
   (rörelsen ÄR informationen om att något pågår). */
@media (prefers-reduced-motion: reduce) {
  :root,
  [data-ton] {
    --duration-instant: 1ms;
    --duration-fast: 1ms;
    --duration-normal: 1ms;
    --duration-slow: 1ms;
    --duration-slower: 1ms;
    --duration-slower-ut: 1ms;
    --duration-ambient: 1ms;
    --delay-ambient: 0ms;
    --ease-spring: var(--ease-out);
    --entre-forflyttning: 0px;
    --mask-start: 0%;
    --ambient-loop: paused;
    --nav-auto-dolj: 0;
    --knapp-tryck: 0px;
  }
}
