﻿/*
Theme Name: Frontness App
Author: Frontness
Description: Frontness-appen. Firebase-auth, projekt och push via wpmobile.
Version: 26.0
*/

/* =========================================================
   FRONTNESS APP
   Designspråk hämtat från frontness.se: Onest, gräddvit bas,
   bläcksvart som kontrastyta, terrakotta som accent, pillerknappar
   och stora radier. Här översatt till en mobil app-yta.
   ========================================================= */

/* ============================ typsnitt ============================
   Onest, självhostad. En variabel fil täcker 400–700. Uppdelad i latin
   och latin-ext med unicode-range, precis som på webbplatsen, så den
   extra filen bara hämtas när ett tecken utanför latin faktiskt används. */
@font-face {
  font-family:'Onest';
  font-style:normal;
  font-weight:400 700;
  font-display:swap;
  src:url('fonts/onest-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family:'Onest';
  font-style:normal;
  font-weight:400 700;
  font-display:swap;
  src:url('fonts/onest-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ============================= tokens ============================= */
:root {
  --background:#f4f3f0;
  --foreground:#111111;
  --ink:#0a0a0a;
  --muted:#8d8d8d;
  --subtle:#b6b6b6;
  --line:#e6e5e2;
  --surface:#f1f0ee;
  --surface-2:#e3e2df;
  --card:#ffffff;
  --accent:#b15f2c;
  --accent-from:#cf8047;
  --accent-to:#97501f;
  --danger:#b3261e;
  --ok:#2f6b46;

  /* Bläckets ton som råa tal, för varje halvgenomskinlig användning:
     rgba(var(--ink-rgb), .5) är text, kanter och hovringar i samma familj.
     I mörkt läge byts talen och hela familjen följer med.

     Genomskinligheten är kalibrerad mot iOS mörka läge, som är det de
     flesta har bredvid appen på samma skärm. Där är secondaryLabel 60 %
     och tertiaryLabel 30 %, och samma alfa läses helt olika åt de två
     hållen: .45 bläck på cremen är en tydlig underrubrik, .45 på svart är
     grått i grått. Underrubrikerna och etiketterna ligger därför på
     .55 till .66 och de svagaste tecknen — platshållare, chevroner — på
     .3 och neråt, där de motsvarar tertiaryLabel. Ändra inte ett enskilt
     tal utan att titta på båda lägena. */
  --ink-rgb:17,17,17;

  /* Den aktiva fyllningen: mörka knappar, valda piller, egna bubblor.
     Skild från --ink med flit — bläcket är också bakgrund på ytor som
     alltid är mörka (startskärmen, topplisten, grinden), och de ska inte
     invertera med temat. Fyllningen ska. */
  --primary:var(--ink);
  --on-primary:#ffffff;

  /* Topplistens logotyp är en vit fil. Den vänds i ljust läge — alltså
     tvärtom mot --logoInvert, som finns för mörk konst på ljus botten. */
  --logoOnBar:invert(1);

  /* Etiketternas två sätt att bära färg.

     I ljust läge är plattan tonad och färgen ligger i texten: mot cremen
     räcker tolv procent för att ringa in ordet, och en fylld platta hade
     lästs som en knapp mitt i en lista.

     I mörkt läge byter de plats. En tonad grön platta på svart blir nästan
     osynlig och ordet i den en gissning, medan en fylld platta i samma
     gröna läses på en meters håll — som statusmärkena i iOS. Samma två
     färger, bara åt andra hållet. */
  --badge-ok-bg:rgba(47,107,70,.12);
  --badge-ok-fg:var(--ok);
  --badge-accent-bg:rgba(177,95,44,.12);
  --badge-accent-fg:var(--accent-to);
  /* Läst: samma vändning som de andra märkena gör mellan lägena.
     Det bar förut ett dämpat märke i båda temana och blev då det enda
     som inte såg ut som ett märke i mörkt läge. */
  --badge-mute-bg:rgba(var(--ink-rgb),.10);
  --badge-mute-fg:rgba(var(--ink-rgb),.62);

  --radius-pill:9999px;
  --radius-card:1.25rem;
  --radius-control:.875rem;

  /* Höjd på flikraden längst ned på telefon, och på skrivbordets mörka
     topplist. Ingen säkerhetszon i höjden på den första: skalet som appen
     körs i klipper redan underkanten, och då blir zonen inte luft utan
     bortklippt yta. */
  --tabbar:4.25rem;
  --topbar:3rem;
  --top:env(safe-area-inset-top);

  /* Flikradens hela höjd: raden plus hemindikatorns zon under den. Allt som
     ska stå ovanför flikraden mäter mot den här och inte mot --tabbar —
     annars hamnar sista knappen bakom indikatorn i stället för ovanför den.

     Den räknas ut där den STÅR, inte där den används. Här stod förr
     motsatsen, och det var fel: en var() inne i en egen egenskap byts ut
     när egenskapen beräknas på sitt eget element — här :root — och det
     färdiga värdet ärvs sedan ned som text. Att sätta om --tabbar till 0px
     längre in når alltså inte hit.

     Följden var 68 px låtsad flikrad på skrivbordet: kroppens
     bottenstoppning, kvittot, brödrostarna och Publicera-knappen mätte
     alla mot en rad som inte fanns, och listan rullade synlig i glappet
     under knappen. Därför sätts --tabbarTotal om tillsammans med --tabbar,
     på samma element. Se body.is-app #app. */
  /* Hemindikatorns zon — när den finns.

     env(safe-area-inset-bottom) svarar 34 punkter även när indikatorn i
     själva verket ligger UTANFÖR det fönster appen får rita i. Så är det i
     den installerade appen på iOS: fönstret slutar en statusrad för tidigt,
     och zonen den rapporterar hör till en kant som inte är vår. Reserverar
     man plats för den ändå slutar menyn 34 punkter för högt av ett skäl
     som inte finns.

     JS nollställer den i just det läget. Se mätBrist. */
  /* Hur mycket flikraden håller undan för hemindikatorn.

     iOS reserverar 34 punkter, men strecket självt ritas i de nedersta
     fem. Hela zonen blev därför ett tomrum under etiketterna som var
     större än avståndet över dem, och menyn såg ut att sväva. En halv rem
     mindre räcker: strecket har fortfarande trettio punkter för sig
     självt, och raden kommer ned dit den hör hemma.

     max() så att den aldrig blir negativ på en enhet utan indikator, där
     zonen är noll från början. */
  --zonNed:max(env(safe-area-inset-bottom) - .875rem, 0px);
  --tabbarTotal:calc(var(--tabbar) + var(--zonNed));

  /* Det som fattas nedtill i den installerade appen på iOS. Se #app längre
     ned. Noll överallt annars, och JS skriver över den när den behövs. */
  --brist:0px;
}

/* ============================= mörkt läge =========================
   Appens eget tema, inte webbplatsernas: renderaren i site.js rör det
   här aldrig, och förhandsgranskningarna är ljusa tills sajten själv
   säger något annat.

   Två block med samma värden, med flit: det första gäller den som följer
   systemet (inget val gjort — data-theme saknas), det andra den som valt
   mörkt själv. Ett gemensamt block går inte att skriva — en mediefråga
   och en attributväljare kan inte stå i samma regel.

   --ink står kvar mörk: startskärmen, topplisten och installationsgrinden
   är mörka ytor i båda lägena. Det som byter håll är fyllningen
   (--primary) och bläckfamiljen (--ink-rgb), som är text och hovringar. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --background:#000000;
    --foreground:#e9e8e4;
    --muted:#8e8e93;
    --subtle:#545458;
    --line:#2c2c2e;
    --surface:#1c1c1e;
    --surface-2:#2c2c2e;
    --card:#1c1c1e;
    --danger:#e26a60;
    --ok:#5cab7d;
    --ink-rgb:233,232,228;
    --primary:#eceae5;
    --on-primary:#000000;
    --logoInvert:invert(1);
    --badge-ok-bg:#5cab7d;
    --badge-ok-fg:#08170f;
    --badge-accent-bg:#cf8047;
    --badge-accent-fg:#1e1006;
    --badge-mute-bg:#8d938c;
    --badge-mute-fg:#14170f;
    --logoOnBar:none;
  }
}
:root[data-theme="dark"] {
  --background:#000000;
  --foreground:#e9e8e4;
  --muted:#8e8e93;
  --subtle:#545458;
  --line:#2c2c2e;
  --surface:#1c1c1e;
  --surface-2:#2c2c2e;
  --card:#1c1c1e;
  --danger:#e26a60;
  --ok:#5cab7d;
  --ink-rgb:233,232,228;
  --primary:#eceae5;
  --on-primary:#000000;
  --logoInvert:invert(1);
  --badge-ok-bg:#5cab7d;
  --badge-ok-fg:#08170f;
  --badge-accent-bg:#cf8047;
  --badge-accent-fg:#1e1006;
  --badge-mute-bg:#8d938c;
  --badge-mute-fg:#14170f;
  --logoOnBar:none;
}

/* ============================= reset ============================= */
* { box-sizing:border-box; margin:0; padding:0 }

/* Ingen vågrät rullning, någonsin.

   Appen är byggd av fasta lager som ligger i fönstret; blir något ändå en
   pixel för brett — en negativ marginal, en kurva som ritar utanför sin
   ruta — så ska det klippas och inte ge hela sidan en rullningslist i
   sidled. clip och inte hidden: hidden hade gjort kroppen till en
   rullningsbehållare och tagit sticky-beteendet med sig. */
html, body { overflow-x:clip; min-height: 100vh; }
html {
  font-size:16px;
  -webkit-font-smoothing:antialiased;
  -webkit-text-size-adjust:100%;
  background:var(--background);
}
body {
  background:var(--background);
  color:var(--foreground);
  font-family:'Onest',system-ui,-apple-system,'Segoe UI',sans-serif;
  overflow-wrap:break-word;
  overscroll-behavior-y:none;
}
a { color:inherit; text-decoration:none }
button { font:inherit; color:inherit; background:none; border:none; cursor:pointer }
input,textarea,select { font:inherit; color:inherit }
ul { list-style:none }
img,svg { display:block }
[hidden] { display:none !important }
:focus-visible { outline:2px solid var(--accent); outline-offset:2px }
/* Behållare som tar emot fokus med skript ritar ingen ring.

   Vyn och dialogrutan får tabindex="-1" och fokuseras när de öppnas, så
   att skärmläsaren läser upp vad man kommit till i stället för att börja
   om från sidans topp. Men de är inga kontroller, och en accentfärgad
   ram runt en ruta som fyller hela fönstret säger ingenting om var man
   står — den ritades som två lodräta streck längs spaltens kanter och
   såg ut som ett renderingsfel.

   Ringen hör till det man kan trycka på. Uppläsningen finns kvar. */
.screen[tabindex="-1"]:focus,
.screen[tabindex="-1"]:focus-visible,
.sheet__panel[tabindex="-1"]:focus,
.sheet__panel[tabindex="-1"]:focus-visible { outline:none }
@media (prefers-reduced-motion:reduce) { *,*::before,*::after { animation:none !important; transition:none !important }}

/* Textmarkering av på appytor men på i fält — annars känns webbvyn som en
   webbsida i stället för som en app vid långtryck. */
body { -webkit-user-select:none; user-select:none; -webkit-tap-highlight-color:transparent }
input,textarea,[contenteditable="true"],.selectable { -webkit-user-select:text; user-select:text }

.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 }

/* ============================== skärmar ===========================
   Varje vy är en <section>. Push-vyer glider in från höger, flikvyer
   byts utan rörelse. Positionen är fixed så rullningen alltid nollställs
   till toppen när man byter vy. */
.screen {
  position:fixed; inset:0; z-index:10;
  display:none; flex-direction:column;
  background:var(--background);
  /* Rullar i höjd, aldrig i sidled.

     overflow-y:auto ensamt gör overflow-x till auto — det står i specen —
     och då räcker ett enda barn som är en pixel för brett för att hela
     vyn ska gå att dra i sidled. Under Statistik hände just det: kurvan,
     korten och texten låg alla förskjutna åt vänster och rubrikerna var
     avklippta, utan att något av dem hade fel på sig. Kroppen klipper
     redan sitt eget, men skärmen är en egen rullningsbehållare och måste
     säga det själv. */
  /* Ingen -webkit-overflow-scrolling:touch. Den behövdes före iOS 13 för
     att få tröghet i rullningen; sedan dess är tröghet förvalet, och
     egenskapen har i stället en känd baksida: rullaren fryser när en
     förfader byter display eller synlighet, och tinar först vid nästa
     tryck. Det är precis den sortens "rullningen tog slut, jag måste
     trycka en gång till" som gick att råka ut för här. */
  overflow-y:auto; overflow-x:hidden;
  /* Rullningslister hör inte hemma i en app. På telefon finns de inte,
     och i telefonramen på skrivbordet skulle de avslöja att det är en
     webbsida. */
  scrollbar-width:none;
}
.screen::-webkit-scrollbar { display:none }
.screen.is-active { display:flex }

/* Inglidande vyer ligger över rotvyn, som blir kvar synlig bakom dem.
   Därför är de alltid utlagda och flyttas i stället för att döljas. */
.screen--push {
  display:flex; z-index:30; transform:translate3d(100%,0,0);
  /* visibility måste stå med i övergången, annars slår den om direkt.

     Framåt märks det inte — vyn blir synlig i samma stund den börjar
     glida in. Bakåt gömdes den i stället på en gång, och glidningen ut
     skedde på något osynligt: skärmen såg ut att bara försvinna.

     Egenskapen interpoleras stegvis, och steget ligger i slutet när man
     går mot hidden och i början när man går mot visible. Det är precis
     rätt håll åt båda hållen. */
  transition:transform .28s cubic-bezier(.25,.46,.45,.94),
             visibility .28s;
  visibility:hidden;
}
.screen--push.is-active { transform:translate3d(0,0,0); visibility:visible }

/* Vyn man står i ligger över dem den lämnade bakom sig.

   Höjden mellan lagren är annars ordningen i index.php: en vy som öppnas
   ur en annan står längre ned i filen och hamnar därför ovanpå den. Det
   stämmer för allt utom förhandsgranskningen, som står tidigt bland
   vyerna men öppnas ur en flik som står sent — Hemsida, Inkorg,
   Statistik. Den flikvyn ligger kvar aktiv under den, med flit, och
   målades alltså ovanpå det man just öppnat.

   Sätts av activate(), som är det enda stället som vet vilken vy som är
   vyns egen och inte en förälder i kedjan. En nivå räcker: ordningen
   mellan de övriga lagren ska inte röras.

   Skrivbordets egna höjder står högre upp i kaskaden och tar över där de
   gäller — lådor på 50, globala lager på 56, förhandsgranskningen på 60.
   Den här raden är telefonens. */
.screen.is-top { z-index:31 }
/* Ett snäpp över vyn man kommer till, och bara medan den glider ut.
   Utan det gled den ut bakom sin egen förälder och syntes aldrig — se
   glidUt i routern. */
.screen--push.is-leaving { z-index:32 }

/* Projektväljaren ovanpå allt annat, när den öppnas ur ett projekt.

   Den var appens lobby och därmed rotvyn: att öppna den innebar att man
   lämnade projektet, och vyn man stod i lades undan. Nu ligger fliken kvar
   aktiv under väljaren — det är den man kommer tillbaka till — och en
   inglidande vy ligger på 30, flikraden på 40. Utan höjden här hade
   väljaren hamnat under den vy den ska täcka.

   is-over sätts av lobbyLäge och bara när ett projekt är öppet. Är det
   inte det är väljaren fortfarande själva vyn och behöver ingen höjd. */
#screen-projects.is-over { z-index:60 }

/* Två vyer på samma djup byter utan rörelse. Ett flikbyte är inte en
   navigering framåt och ska inte se ut som en. Sätts av routern för en
   bildruta och tas bort igen. */
.is-instant .screen { transition:none !important }

/* Rätt geometri för den installerade appen ligger på #app, inte här.
   Se avsnittet "telefonram" längre ned: på telefon blir #app en box som
   täcker hela skärmen, och varje position:fixed-lager mäts mot den. */

/* ---------- ytorna utanför webbvyn ----------
   Höjden ovan täcker vyn. Kvar är ramen omkring den: iOS målar området
   runt hemindikatorn — och statusraden — med rotens bakgrundsfärg, inte
   med det som råkar ligga överst i sidan. Roten är gräddvit, så på de
   svarta gästvyerna blev det ändå en ljus kant tvärs över underkanten,
   oavsett hur långt ned skärmen själv nådde.

   Alltså får roten följa vyn i stället: bläck så länge starten och
   inloggningen gäller, gräddvit så fort man är inne i appen. Klassen
   sitter från första bildrutan — startskärmen är svart — och routern
   tar den när den vet vem användaren är.

   Bara telefonlayouten. På skrivbordet ligger appen i en ram, och där är
   bakgrunden utanför ramen en egen sak. */
@media (max-width:759px), (max-height:639px) {
  html.is-ink, html.is-ink body { background:var(--ink) }
}

/* ============================== toppfält ==========================
   Sticky, inte fixed: den ska rulla i sin egen sektion, inte över alla. */
.bar {
  position:sticky; top:0; z-index:20;
  flex:0 0 auto;
  display:grid; grid-template-columns:2.75rem 1fr 2.75rem; align-items:center;
  height:calc(3.5rem + var(--top));
  /* Samma sidmarginal som kroppen. Utan den satt knapparna i listen tätt
     mot skärmkanten medan kuggen på Inkorg och Chatt — som ligger i
     kroppen, inte i listen — stod indragen. Två knappar med samma uppgift
     ska stå på samma ställe, oavsett vilken av de två som råkar rita dem. */
  padding:var(--top) 1.25rem 0;
  background:var(--background);
}
.bar__title { text-align:center; font-size:.9375rem; font-weight:600; letter-spacing:-.01em;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis }

/* Rubrik med en uppgift efter sig. Namnet kortas av när utrymmet tryter,
   datumet aldrig — ett halvt datum säger ingenting. */
.bar__title--split { display:flex; align-items:baseline; justify-content:center; gap:.4375rem }
.bar__title--split > span:first-child { min-width:0; overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap }
.bar__meta { flex:0 0 auto; font-size:.75rem; font-weight:400;
  color:rgba(var(--ink-rgb),.5); white-space:nowrap }
.bar__btn { display:inline-flex; align-items:center; justify-content:center; width:2.75rem; height:2.75rem;
  border-radius:var(--radius-pill); color:var(--foreground) }
.bar__btn:active { background:var(--surface-2) }

/* Ikonknapparna dras ut mot kanterna, och olika långt.

   Avståndet som räknas är det till bläcket och inte till knappens ruta.
   Bakåtpilen är ett smalt tecken mitt i sin ruta och kuggen fyller nästan
   hela sin: med samma stoppning stod pilen synligt längre in än kuggen,
   fast de satt lika långt från kanten. Talen är skillnaden mellan rutan
   och tecknet i den, så bläcket hamnar lika långt in på båda sidor. */
/* Flyttade och inte marginalsatta: listen är ett rutnät med fasta spalter,
   och en negativ marginal där gör knappen bredare i stället för att skjuta
   den utåt. */
.bar > .bar__btn:first-child { transform:translateX(-.6875rem) }
.bar > .bar__btn:last-child  { transform:translateX(.6875rem) }
/* Pilen är smalast av tecknen och går längst ut. Väljaren bär .bar__btn
   för att väga lika tungt som raden ovanför — annars vinner den, och
   pilen blir stående med kuggens mått. */
.bar > .bar__btn[data-back]  { transform:translateX(-1.125rem) }
/* Radering är den enda åtgärden i en list som inte går att ångra. Den ska
   se ut som det utan att skrika — dämpad tills man rör vid den. */
.bar__btn--danger { color:rgba(var(--ink-rgb),.5) }
.bar__btn--danger:active { background:rgba(179,38,30,.1); color:var(--danger) }
.bar__btn svg { width:1.375rem; height:1.375rem; stroke:currentColor; fill:none;
  stroke-width:2; stroke-linecap:round; stroke-linejoin:round }
.bar--flush { background:transparent }

/* Autosparningens kvitto, i listens högra cell. Det ska gå att läsa i
   förbifarten och sedan försvinna — inte kräva en blick. */
/* Autosparningens kvitto. Ett enda lager för hela appen, ovanför flikraden:
   allt sparar sig självt nu, och ett kvitto per skärm hade blivit sju
   likadana rader att hålla i synk.

   Det ska gå att läsa i förbifarten och sedan försvinna — inte kräva en
   blick, och aldrig ligga i vägen för det man skriver. */
.saveNote {
  position:fixed; left:50%; z-index:60;
  bottom:calc(var(--tabbarTotal) + .5rem);
  padding:.4375rem .875rem; border-radius:var(--radius-pill);
  background:var(--primary); color:var(--on-primary);
  font-size:.75rem; white-space:nowrap;
  opacity:0; transform:translate(-50%,.5rem); pointer-events:none;
  transition:opacity .18s, transform .18s;
}
.saveNote.is-on { opacity:1; transform:translate(-50%,0) }
.saveNote--ok  { background:var(--ok) }
.saveNote--err { background:var(--danger) }

/* Utan flikrad sitter den mot skärmkanten i stället. */
body:not(.has-tabs) .saveNote { bottom:calc(1rem + env(safe-area-inset-bottom)) }

/* ============================== innehåll ==========================
   Botteninlägget håller sista knappen fri från flikraden. */
.body {
  flex:1 1 auto;
  padding:0 1.25rem calc(var(--tabbarTotal) + 1.5rem);
}
.body--noTabs { padding-bottom:calc(1.5rem + env(safe-area-inset-bottom)) }

/* Förhandsgranskningen är sajten och ingenting annat: ingen stoppning,
   rutan fyller allt under listen. */
/* Kroppen är en spalt: lägesväljaren överst, rutan under. Den var en rad
   med bara ramen i, och en väljare i en rad hade lagt sig BREDVID sajten
   i stället för ovanför den. */
/* position:relative för temabandet, som ligger absolut i underkanten.
   Se .previewTema. */
.previewBody { position:relative; padding:0; display:flex;
  flex-direction:column; align-items:stretch }
.previewFull { display:block; flex:1 1 auto; width:100%; border:0; background:#fff }
.previewSeg { align-self:center; margin:.75rem 0 }

/* Mobilläget är en telefon mitt på ytan, inte en smalare sida.
   Ramen behåller sin egen bredd i pixlar — sajten ska mäta 390 och tro att
   den står i en telefon, så mediefrågorna slår om på riktigt. Hade vi bara
   krympt vyn hade den fortsatt rita skrivbordslayouten, bara mindre. */
body.prev-mobile .previewBody .previewFull {
  width:390px; max-width:100%; align-self:center;
  margin-bottom:1rem; border-radius:1.5rem;
  box-shadow:0 1px 2px rgba(17,17,17,.06), 0 18px 40px -20px rgba(17,17,17,.35);
}

/* Vyer utan topplist bär sitt eget toppavstånd. Listen höll undan
   statusraden och urklippet; tas den bort måste något annat göra det, och
   rubriken ska inte ligga i kanten på en telefon utan ram heller.

   Avståndet ligger i rubrikraden och inte i kroppen ovanför den. Skälet
   är att rubrikraden fastnar i överkanten när man rullar: låg höjden i
   kroppen rullade den bort, och rubriken lade sig ovanpå klockan och
   batteriet med sökfältet efter sig. Ligger den i det som fastnar följer
   den med, och rubriken stannar under statusraden hur långt man än rullar.

   Båda vyerna som har klassen — Inkorg och Sidor/Inlägg/Media — inleds med
   en .screenHead. Skulle en tredje inte göra det bär den sitt avstånd
   själv, och därför står regeln på barnet och inte som en reserv. */
.body--top { padding-top:0 }
.body--top > .screenHead { padding-top:calc(var(--top) + 1rem) }

/* ============================== typografi ========================= */
/* 700 och inte 600. Rubriken är skärmens enda ord i sin storlek och ska
   bära den tyngden — 600 läste sig som mellanting mot Onests egen 700. */
.h1 { display:block; font-size:2rem; font-weight:700; letter-spacing:-.03em; line-height:1.08 }
.h2 { display:block; font-size:1.375rem; font-weight:600; letter-spacing:-.02em; line-height:1.15 }
.lead { margin-top:.75rem; font-size:.9375rem; line-height:1.6; color:rgba(var(--ink-rgb),.66) }
.sectionTitle { margin:2.25rem 0 .875rem; font-size:.6875rem; font-weight:600;
  text-transform:uppercase; letter-spacing:.08em; color:rgba(var(--ink-rgb),.55) }

/* En rubrik som ska läsas som en rubrik. Versaler och spärrad text gör en
   etikett — och en etikett över en lista av etiketter säger ingenting om
   vad som är vad. */
.sectionTitle--plain { margin:1.75rem 0 .375rem; font-size:1.0625rem;
  font-weight:600; letter-spacing:-.015em; text-transform:none;
  color:var(--foreground) }
.muted { color:rgba(var(--ink-rgb),.62) }
.small { font-size:.8125rem; line-height:1.55 }

/* ============================== knappar ===========================
   Piller, som på webbplatsen. Full bredd i appen eftersom de nästan
   alltid är sidans enda åtgärd. */
.btn {
  position:relative;
  display:inline-flex; align-items:center; justify-content:center; gap:.5rem;
  width:100%; min-height:3.25rem; padding:.875rem 1.75rem;
  border-radius:var(--radius-pill);
  font-size:.9375rem; font-weight:500; text-align:center;
  transition:opacity .2s, background .2s, box-shadow .2s;
}
.btn:disabled { opacity:.55; cursor:default }
.btn:active:not(:disabled) { opacity:.85 }

.btn--dark { background:var(--primary); color:var(--on-primary) }
.btn--light { background:#fff; color:var(--ink) }
/* Den enda fyllda accentknappen. Sparad för det ena steget man vill att
   någon ska ta — kopplingen av en egen domän — och inte för vanligt
   sparande: en färg som betyder "gör det här" slutar betyda något så
   fort två knappar bär den på samma skärm. */
.btn--accent { background:var(--accent); color:#fff }
.btn--accent:hover:not(:disabled) { background:var(--accent-to) }
.btn--ghost { background:transparent; color:var(--foreground); box-shadow:inset 0 0 0 1px rgba(var(--ink-rgb),.35);
  transition:background .15s, box-shadow .15s }
.btn--ghost:active:not(:disabled) { background:rgba(var(--ink-rgb),.045); box-shadow:inset 0 0 0 1px rgba(var(--ink-rgb),.6) }
/* Konturknappen svarar på pekaren.
   Den hade bara :active, alltså kvitto först när man redan tryckt. På ett
   skrivbord är hovringen det som säger att något ÄR en knapp — utan den
   såg raden med Visa hemsidan och Byt projekt ut som text i en ram.
   Samma dämpade platta som kopieringsknappen och listraderna använder. */
@media (hover:hover) and (pointer:fine) {
  .btn--ghost:hover:not(:disabled) {
    background:rgba(var(--ink-rgb),.055);
    box-shadow:inset 0 0 0 1px rgba(var(--ink-rgb),.5);
  }
}
.btn--plain { background:none; color:rgba(var(--ink-rgb),.66); font-size:.875rem; min-height:2.75rem }
/* Två röda nivåer med olika uppgift: .btn--danger är den lågmälda ingången
   till en farlig åtgärd, .btn--destructive är bekräftelsen i dialogen — den
   enda knappen i appen som är fylld i rött.

   Bredden är innehållets och inte spaltens. Piller-knapparna är fullbreda
   för att de är skärmens åtgärd — men den här är en rad text utan fyllning,
   och full bredd gav den en osynlig träffyta tvärs över hela formuläret.
   Att råka radera ett kort på väg att trycka bredvid ordet är inte ett
   misstag användaren gjorde. Centrerad står den kvar där den stod.

   Gäller inte .btn--plain: den står nästan alltid som andrahandsval under
   en fullbred primärknapp — i arket, i inloggningen, i förslagets rad —
   och ska väga lika mycket som den den står under. */
.btn--danger { background:none; color:var(--danger); font-size:.875rem; min-height:2.75rem;
  display:flex; width:fit-content; margin-inline:auto;
  padding-left:1.5rem; padding-right:1.5rem }
.btn--destructive { background:var(--danger); color:#fff }
.btn--sm { min-height:2.5rem; padding:.5rem 1.125rem; width:auto; font-size:.8125rem }

/* Ikon efter etiketten i en knapp. Pilen betyder att knappen leder vidare
   och inte utför något på plats — därför sitter den efter ordet, inte före. */
.btn__ico { flex:0 0 auto; width:1rem; height:1rem; margin-right:-.125rem;
  stroke:currentColor; fill:none; stroke-width:2;
  stroke-linecap:round; stroke-linejoin:round }

/* En knapp som bara är sin ikon.
   .btn är width:100% och bred stoppning — rätt för en knapp med ord i, men
   en ikon i den blir en tom platta med en prick i mitten. Här sätts bredden
   av höjden i stället, så knappen blir kvadratisk och står som en granne
   till den bredvid och inte som en halv rad. Ikonen tappar sin
   högermarginal, som finns för att stå intill text. */
.btn--icon { width:auto; flex:0 0 auto; aspect-ratio:1; padding:0 }
.btn--icon .btn__ico { width:1.125rem; height:1.125rem; margin-right:0 }

/* Publiceringsraden: Publicera först, ögat sist.
   Publicera tar all plats som blir över — det är den man siktar på — och
   ögat står bredvid. Ensamt lägger det sig i mitten; en ensam knapp i
   vänsterkanten ser ut att ha tappat sin granne. */
.pubRow { display:flex; align-items:stretch; gap:.75rem }
.pubRow .btn--dark { flex:1 1 auto }
/* Ögat är bredare än högt.
 *
 * Kvadraten gjorde det till en cirkel med ett litet tecken i, och bredvid
 * en knapp som spänner över hela raden lästes den som en prick snarare
 * än som en knapp. Ett tecken behöver luft omkring sig för att synas som
 * ett val: bredden är satt, inte ärvd från höjden, och tecknet självt är
 * ett par pixlar större. Gapet till Publicera är .75 rem av samma skäl —
 * .5 rem lät dem se ut att höra ihop som en enda kontroll. */
.pubRow .btn--icon {
  aspect-ratio:auto; width:4.25rem; flex:0 0 auto; padding:0;
}
.pubRow .btn--icon .btn__ico { width:1.375rem; height:1.375rem }
.pubRow--ensam { justify-content:center }
/* Ensam bär knappen sitt ord och hugger om det. Full bredd hade gett en
   grå stapel tvärs över raden med samma tyngd som Publicera har när den
   finns — och det är inte samma sorts knapp. Se publishCta. */
.pubRow--ensam .btn { width:auto; flex:0 0 auto }
.btn--sm .btn__ico { width:.9375rem; height:.9375rem }

/* Knappen behåller sin bredd när texten byts mot en snurra: texten ligger
   kvar men görs osynlig, i stället för att rivas ut ur knappen. */
.btn.is-busy .btn__label { visibility:hidden }
.btn__spin { display:none; position:absolute; width:1.125rem; height:1.125rem;
  border:2px solid currentColor; border-bottom-color:transparent; border-radius:50%;
  animation:spin .6s linear infinite }
.btn.is-busy .btn__spin { display:block }
@keyframes spin { to { transform:rotate(360deg) }}

/* Fristående snurra, för väntan som inte hör till en knapp. Samma rörelse
   som knappens, så det läses som samma sorts väntan. */
.spin { display:block; width:1rem; height:1rem; border:2px solid currentColor;
  border-bottom-color:transparent; border-radius:50%;
  animation:spin .6s linear infinite }

/* Väntan i en tomruta, och väntan på en rad. Se waitBox och waitRow: en
   text som säger "hämtar" utan rörelse läser som ett meddelande och inte
   som ett tillstånd. Snurran är dämpad — den ska synas som pågående, inte
   konkurrera med det som kommer sedan. */
.empty .spin { margin:0 auto .625rem; color:rgba(var(--ink-rgb),.45) }
.waiting { margin:0; display:flex; align-items:center; gap:.5rem; line-height:1.6 }
.waiting .spin { flex:0 0 auto; width:.875rem; height:.875rem;
  color:rgba(var(--ink-rgb),.45) }

@media (prefers-reduced-motion:reduce) {
  /* Den som bett om mindre rörelse ska ändå se ATT något pågår. Snurran
     står still och pulsar i stället; en helt stilla ring hade varit precis
     den text vi just gjorde om. */
  .spin { animation:spinPuls 1.4s ease-in-out infinite }
  @keyframes spinPuls { 0%,100% { opacity:1 } 50% { opacity:.35 }}
}

.stack { display:flex; flex-direction:column; gap:.75rem }

/* Raden under knappen. Meningen är text, inte ett mål — bara de tre orden
   längst ut bär vägen vidare, och strecket säger var den går. */
.stackNote { margin-top:.25rem; text-align:center;
  font-size:.875rem; line-height:1.5; color:rgba(var(--ink-rgb),.66) }
.ink .stackNote { color:rgba(255,255,255,.6) }
.textLink {
  padding:0; border:0; background:none; cursor:pointer;
  font:inherit; color:inherit;
  text-decoration:underline; text-underline-offset:.2em;
  text-decoration-color:currentColor;
}
.ink .textLink { color:#fff }
.textLink:focus-visible { outline:2px solid var(--accent); outline-offset:3px; border-radius:.2rem }
.row { display:flex; align-items:center; gap:.75rem }

/* ============================== formulär ========================== */
/* Tätt inuti, luftigt emellan.

   Etiketten och fältet den hör till är EN sak och sitter ihop; nästa fält
   är en annan. När avståndet mellan dem var nästan detsamma som inuti dem
   blev formuläret en enda spalt av rader, och ögat fick leta efter var ett
   fält slutade. Nu är gapet mellan fälten drygt tre gånger så stort som
   det inuti, vilket är vad som gör dem till grupper. */
.field { display:flex; flex-direction:column; gap:.4375rem; margin-bottom:1.5rem }
.field > label { display:flex; align-items:center; gap:.375rem;
  font-size:.6875rem; font-weight:600; text-transform:uppercase;
  letter-spacing:.06em; color:rgba(var(--ink-rgb),.62) }

/* Hjälptexten bakom ett i.

   Knappen är liten men har hela etikettens radhöjd att träffas på, och
   bubblan fälls fram vid pekning och vid tangentbordsfokus — det senare
   är det enda sättet den som inte pekar kommer åt texten.

   Bubblan ligger under knappen och inte över: fälten står tätt, och en
   bubbla uppåt hade lagt sig över etiketten den förklarar. */
.tip {
  position:relative; flex:0 0 auto;
  display:grid; place-items:center;
  width:.9375rem; height:.9375rem; padding:0;
  border:1px solid rgba(var(--ink-rgb),.3); border-radius:50%;
  background:none; color:rgba(var(--ink-rgb),.55);
  font-size:.5625rem; font-weight:700; line-height:1;
  font-family:inherit; text-transform:none; letter-spacing:0;
  cursor:help;
}
.tip:hover { border-color:rgba(var(--ink-rgb),.5); color:var(--foreground) }
.tip::after {
  content:attr(data-tip);
  position:absolute; top:calc(100% + .4375rem); left:-.5rem; z-index:20;
  width:max-content; max-width:15rem;
  padding:.5rem .625rem; border-radius:.5rem;
  background:var(--surface-2); color:var(--foreground);
  box-shadow:0 .5rem 1.5rem -.5rem rgba(0,0,0,.55),
             inset 0 0 0 1px rgba(var(--ink-rgb),.12);
  font-size:.75rem; font-weight:400; line-height:1.45;
  letter-spacing:0; text-transform:none; text-align:left;
  white-space:normal;
  opacity:0; visibility:hidden; transform:translateY(-.25rem);
  transition:opacity .12s ease, transform .12s ease, visibility .12s;
}
/* :focus och inte :focus-visible. Den senare gäller tangentbord, och på
   en telefon finns ingen hovring — ett tryck ger fokus, och det är enda
   sättet texten går att få fram där. */
.tip:hover::after, .tip:focus::after {
  opacity:1; visibility:visible; transform:none;
}

.ctrl {
  width:100%; border:1px solid var(--line); background:var(--card);
  border-radius:var(--radius-control); padding:.9375rem 1rem;
  font-size:1rem; /* under 16px zoomar iOS in vid fokus */
  outline:none; transition:border-color .2s;
}
textarea.ctrl { resize:none; min-height:6rem; line-height:1.55 }

/* Datum- och tidsfält.

   iOS ritar dem inte som fält utan som en egen liten redigerare, och den
   krymper inte. Fältet bär width:100% och border-box som alla andra, men
   det inre innehållet kräver mer plats än spalten har — och då sticker
   rutan ut ur kortet den ligger i. Samma redigerare ger värdet en egen
   radhöjd ovanpå fältets stoppning, vilket gör rutan nästan dubbelt så
   hög som grannarna.

   appearance:none lämnar tillbaka layouten till stilmallen, min-width:0
   låter fältet bli så smalt som spalten är, och pseudoelementen nollas så
   de inte lägger sina egna mått ovanpå .ctrl:s. Värdet vänstersställs:
   iOS högerställer det, vilket i en rad med etikett till vänster ser ut
   som att fältet är tomt. */
input[type="date"].ctrl,
input[type="datetime-local"].ctrl,
input[type="time"].ctrl {
  -webkit-appearance:none; appearance:none;
  min-width:0; max-width:100%; height:auto; line-height:1.2;
}
.ctrl::-webkit-date-and-time-value { text-align:left; margin:0; min-height:0 }
.ctrl::-webkit-datetime-edit { padding:0 }
.ctrl:focus { border-color:rgba(var(--ink-rgb),.35) }
.ctrl::placeholder { color:rgba(var(--ink-rgb),.3); opacity:1 }
/* Sökfältets platshållare är inte en antydan om vad man kan skriva utan
   namnet på vad rutan gör. Tre tiondelars bläck läste sig som avstängd. */
.ctrl--search::placeholder { color:var(--foreground); opacity:1 }
.ctrl:disabled { background:var(--surface); color:rgba(var(--ink-rgb),.58) }

/* Ögat i lösenordsfältet.
   Omslaget sitter runt enbart inmatningen, inte runt hela .field — annars
   räknas etiketten och hjälptexten in i höjden och ögat hamnar under fältet. */
.field__wrap { position:relative; display:block }
.field__wrap .ctrl { padding-right:3.25rem }
.field__eye { position:absolute; right:.25rem; top:50%; transform:translateY(-50%);
  width:2.75rem; height:2.75rem;
  display:inline-flex; align-items:center; justify-content:center; color:rgba(var(--ink-rgb),.55) }
.field__eye svg { width:1.25rem; height:1.25rem; stroke:currentColor; fill:none;
  stroke-width:1.75; stroke-linecap:round; stroke-linejoin:round }
.field__eye.is-on { color:var(--foreground) }

.field__hint { font-size:.75rem; color:rgba(var(--ink-rgb),.55) }

/* Rad som öppnar en egen väljarskärm. Ser ut som ett fält, beter sig som
   en länk — värdet står där ett värde brukar stå. */
.pickRow {
  display:flex; align-items:center; gap:.75rem; width:100%; text-align:left;
  border:1px solid var(--line); background:var(--card);
  border-radius:var(--radius-control); padding:.9375rem 1rem;
  transition:border-color .2s;
}
.pickRow:active { border-color:rgba(var(--ink-rgb),.35) }
.pickRow__value { flex:1 1 auto; min-width:0; font-size:1rem;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis }

/* En rad i typsnittslistan: namnet i typsnittet, och en mening under så
   man ser hur det faktiskt läser sig. */
.fontRow__name { display:block; font-size:1.0625rem; font-weight:600; letter-spacing:-.01em;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis }
.fontRow__sample { display:block; margin-top:.1875rem; font-size:.9375rem;
  color:rgba(var(--ink-rgb),.58); white-space:nowrap; overflow:hidden; text-overflow:ellipsis }

/* ============================== switch ============================
   Av eller på, med etiketten som tryckyta. Hela raden är en <label>, så
   tummen träffar rätt utan att någon behöver sikta på reglaget. */
.switchRow { display:flex; align-items:center; gap:1rem; padding:.75rem 0; cursor:pointer }
.switchRow + .switchRow { border-top:1px solid var(--line) }
.switchRow__text { flex:1 1 auto; min-width:0 }
.switchRow__label { display:block; font-size:.9375rem; font-weight:500; letter-spacing:-.01em }
.switchRow__hint { display:block; margin-top:.125rem; font-size:.75rem; line-height:1.4;
  color:rgba(var(--ink-rgb),.55) }
.switch {
  flex:0 0 auto; position:relative; width:3rem; height:1.75rem; margin:0;
  border-radius:999px; background:rgba(var(--ink-rgb),.16); cursor:pointer;
  -webkit-appearance:none; appearance:none; transition:background .2s;
}
.switch::after {
  content:''; position:absolute; top:.1875rem; left:.1875rem;
  width:1.375rem; height:1.375rem; border-radius:50%; background:#fff;
  box-shadow:0 1px 3px rgba(10,10,10,.25); transition:transform .2s;
}
/* Grönt när det är på. Accentfärgen betyder "det här är vår färg", inte
   "det här fungerar" — och en switch svarar på det andra. */
.switch:checked { background:var(--ok) }
.switch:checked::after { transform:translateX(1.25rem) }

/* Reglage som hör ihop men inte ligger i ett kort. Hårstrecket mellan
   raderna räcker som gruppering; luften skiljer dem från fältet ovanför. */

/* Färgruta + textfält, och sifferfält + enhet. Samma rad, två kontroller
   för ett värde: rutan väljer, texten tar en exakt kod. */
.colorRow { display:flex; align-items:center; gap:.625rem }
.colorRow .ctrl { flex:1 1 auto; min-width:0 }
.colorRow__unit { flex:0 0 auto; font-size:.8125rem; color:rgba(var(--ink-rgb),.55) }
.colorRow__dot {
  flex:0 0 auto; width:3rem; height:3.0625rem; padding:0;
  border:1px solid var(--line); border-radius:var(--radius-control);
  background:var(--card); cursor:pointer; -webkit-appearance:none; appearance:none;
}
/* Webkit ritar färgrutan som en knapp med egen inre marginal. Utan det här
   flyter färgen inte ut i hela rutan. */
.colorRow__dot::-webkit-color-swatch-wrapper { padding:.25rem }
.colorRow__dot::-webkit-color-swatch { border:none; border-radius:.375rem }
.colorRow__dot::-moz-color-swatch { border:none; border-radius:.375rem }

/* Fält på mörk yta (auth-skärmarna). */
.onInk .field > label { color:rgba(255,255,255,.55) }
/* Solid och inte genomskinlig: .onInk möter två olika bottnar — telefonens
   #0a0a0a och gästlayoutens #000 — och en slöja hade gett fälten två olika
   toner. Värdet är det den gamla slöjan gav över telefonens botten. */
.onInk .ctrl { background:#191919; border-color:rgba(255,255,255,.16); color:#fff }
.onInk .ctrl:focus { border-color:rgba(255,255,255,.45) }
.onInk .ctrl::placeholder { color:rgba(255,255,255,.32) }
.onInk .field__eye { color:rgba(255,255,255,.5) }
.onInk .field__eye.is-on { color:#fff }
.onInk .field__hint { color:rgba(255,255,255,.45) }

/* ============================== kort ============================== */
.card {
  background:var(--card); border:1px solid var(--line);
  border-radius:var(--radius-card); padding:1.125rem 1.25rem;
}
.card + .card { margin-top:.75rem }

/* Ett kort som är en väg vidare.

   Samma kort som de andra, men med en pil i kanten och hela ytan som
   träffyta — den som ser sitt namn högst upp på Konto ska kunna trycka
   där för att ändra det, inte leta efter ett fält längre ned. */
.card--row {
  display:flex; align-items:center; justify-content:space-between; gap:.75rem;
  width:100%; text-align:left; cursor:pointer;
}
.card--row:active { background:var(--surface-2) }
.card--row .tile__sub { color:rgba(var(--ink-rgb),.55) }
/* Medlemskortet som knapp. Hela kortet är tryckytan, som en rad i en
   lista — men det bär två rader text under raden och kan därför inte
   vara .card--row, som lägger allt på en linje. Pilen står i raden. */
button.memberCard { display:block; width:100%; text-align:left; cursor:pointer }
button.memberCard:active { background:var(--surface-2) }
.memberCard .navrow__chev { flex:0 0 auto; margin-left:.25rem }

/* En anslutning: ett kort med tjänstens namn överst, kanalerna under
   och en diskret väg ut nederst. Ett kort per anslutning, så att fler
   tjänster kan stå under varandra med samma form. */
.conn__head { display:flex; align-items:center; gap:.625rem; margin-bottom:.875rem }
.conn__ico { width:1.25rem; height:1.25rem; flex:0 0 auto; stroke:currentColor;
  fill:none; stroke-width:1.75; stroke-linecap:round; stroke-linejoin:round;
  color:rgba(var(--ink-rgb),.7) }
.conn__foot { display:flex; align-items:center; justify-content:space-between; gap:1rem;
  margin-top:1rem; padding-top:.875rem; border-top:1px solid var(--line) }
.conn__foot .btn { flex:0 0 auto; width:auto; min-width:0; min-height:2.25rem; padding:.375rem .75rem }

/* Hemsidans underval i statistiken: Live, Besök, Synlighet som en
   mindre rad överst i höljet. Lägre än flikraden ovanför, så de läses
   som ett val inom fliken och inte som en andra flikrad. */
.seg.seg--tabs.statSub { margin:0 0 1rem; padding:0; gap:0; background:none; border:0;
  border-radius:0; border-bottom:1px solid var(--line) }
/* Understrukna flikar och inte piller: två pillerrader ovanpå varandra
   läses som två likvärdiga val, och det här är ett underval. Den aktiva
   bär textfärgen — vit i mörkt läge — i både ord och streck, och strecket
   ligger på linjen under raden.

   Trippelklassen är specificiteten: .seg button.is-on och .seg--tabs
   button står tidigare i filen med två klasser var, och en regel med
   en klass förlorade mot dem fast den stod sist. */
.seg.seg--tabs.statSub button { height:2.25rem; margin-bottom:-1px; border-radius:0;
  border-bottom:2px solid transparent; background:none; font-size:.8125rem;
  color:rgba(var(--ink-rgb),.6) }
.seg.seg--tabs.statSub button.is-on { background:none; color:var(--foreground);
  border-bottom-color:var(--foreground); font-weight:600 }

/* Samma avstånd när ett tomt läge följer på ett kort. I statistiken står
   "Inga besök i perioden" direkt under kurvkortet, och utan regeln låg
   de dikt an mot varandra. */
.card + .placeholder, .card + .empty { margin-top:.75rem }

/* Kontot längst ned under Inställningar. Samma kort som kontoskärmens
   eget, så det syns att de leder till samma ställe.

   Bara på telefon. Från 1100 px står brickan i topplisten och gäller varje
   vy — två vägar till samma ställe på samma skärm är en för mycket. Luften
   över är sektionsavståndet: kortet hör inte till listan ovanför. */
/* Kortet står först på skärmen numera och behöver inget avstånd uppåt —
   kroppen bär redan sitt. Marginalen nedåt skiljer det från rubriken
   Projekt: kortet gäller en själv, listan under gäller sajten. */
/* 1,25 plus rubrikens egna .5 ger 1,75 rem emellan — samma luft som
   mellan skärmens andra avsnitt. Marginalerna faller inte ihop: kortet är
   en flexrad och inte ett vanligt block. */
.settingsAccount { margin:.5rem 0 1.25rem }
@media (min-width:1100px) { body.is-app .settingsAccount { display:none } }

/* Klickbar rad i en lista. */
.tile {
  display:flex; align-items:center; gap:1rem; width:100%; text-align:left;
  background:var(--card); border:1px solid var(--line);
  border-radius:var(--radius-card); padding:1rem 1.125rem;
  transition:background .15s;
}
.tile + .tile { margin-top:.75rem }
.tile:active { background:var(--surface) }
/* Projektet man redan står i.

   Accentkant och ingen pil: pilen betyder "gå hit", och man är redan här.
   Raden under säger det med ord för den som inte läser en färgad kant som
   ett besked. */
/* Olästa i ett projekt man inte står i. Samma cirkel som räknarna i
   menyn och i flikarna: ett tal som ändrar sig bär färg som yta.

   Står före pilen och inte efter — pilen säger "hit går du", siffran säger
   varför, och skälet ska komma före vägen. */

.tile__text { display:block; min-width:0; flex:1 1 auto }
/* display:block krävs — raderna är <span> i märkspråket, och
   text-overflow:ellipsis biter inte på ett inline-element. */
.tile__title { display:block; font-size:.9375rem; font-weight:500; letter-spacing:-.01em;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis }
.tile__sub { display:block; margin-top:.125rem; font-size:.8125rem; color:rgba(var(--ink-rgb),.58);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis }
/* Rubriken i projektvyn får brytas — den är vyns huvudsak, inte en listrad. */
.row .tile__text .h2 { white-space:normal }
.tile__chev { flex:0 0 auto; width:1.125rem; height:1.125rem; color:rgba(var(--ink-rgb),.3);
  stroke:currentColor; fill:none; stroke-width:2; stroke-linecap:round; stroke-linejoin:round }

/* ========================== förhandsgranskning =====================
   Iframen ritas i sin riktiga bredd och skalas ner med transform. Scenen
   klipper och bär höjden, som JS räknar ut ur skalan. */
.preview {
  position:relative; overflow:hidden;
  /* Ut ur kroppens sidopadding. Sidan som visas är det enda som betyder
     något här, och varje millimeter marginal är en millimeter mindre sajt
     att titta på. Sidokanterna och rundningen följer med ut: en ram som
     går utanför skärmen har inga hörn att runda. */
  width:auto; margin-left:-1.25rem; margin-right:-1.25rem;
  border:1px solid var(--line); border-left:0; border-right:0;
  background:#fff;
}
.preview__frame { display:block; border:0; background:#fff }

/* ============================== lista =============================
   Rader direkt på bakgrunden, utan kort omkring sig. Ett kort ger varje rad
   en egen ram att titta på, och tio ramar under varandra är tio saker att
   titta på i stället för en lista.

   Hårstrecket börjar vid texten och inte vid kanten. Då läses ikonerna som
   en kolumn i stället för som en rad lösa rutor, och listan blir tystare ju
   längre den är i stället för mer högljudd. */
.list { margin-bottom:.5rem }

/* Hårstrecket går kant till kant. Det låg indraget till textkolumnen när
   raden hade en ikonruta att rätta in sig efter; utan den blev indraget
   bara ett handtag brett åt vänster och noll åt höger, vilket läses som
   ett misstag. */
.listRow { display:flex; align-items:stretch; width:100% }
.listRow + .listRow { border-top:1px solid var(--line) }

.listRow__open { flex:1 1 auto; min-width:0; display:flex; align-items:center;
  gap:.875rem; padding:.875rem 0; text-align:left; border-radius:.75rem;
  transition:background .15s }
.listRow__open:active { background:var(--surface) }

.listRow__body { flex:1 1 auto; min-width:0 }

.listRow__head { display:flex; align-items:baseline; gap:.625rem; width:100%;
  text-align:left }
.listRow__title { flex:1 1 auto; min-width:0; font-size:.9375rem; font-weight:600;
  letter-spacing:-.01em; white-space:nowrap; overflow:hidden; text-overflow:ellipsis }
/* Datumet läses lika ofta som underrubriken bredvid och ska väga lika
   mycket: i iOS meddelandelista står tidpunkten i samma grå som
   förhandsvisningen, inte ett steg svagare. */
.listRow__meta { flex:0 0 auto; font-size:.75rem; color:rgba(var(--ink-rgb),.58);
  font-variant-numeric:tabular-nums; text-align:right }

/* Klockslaget under datumet. Ljusare, för det är den mindre av de två
   uppgifterna — man letar efter dagen först och tiden sedan. */
.listRow__metaSub { display:block; margin-top:.125rem;
  color:rgba(var(--ink-rgb),.32) }
.listRow__chev { flex:0 0 auto; width:1rem; height:1rem;
  color:rgba(var(--ink-rgb),.28); stroke:currentColor; fill:none; stroke-width:2;
  stroke-linecap:round; stroke-linejoin:round }
.listRow__sub { display:flex; align-items:center; gap:.375rem;
  margin-top:.1875rem; font-size:.8125rem; line-height:1.45;
  color:rgba(var(--ink-rgb),.58) }
/* Texten klipps, inte raden: tecknet ska stå kvar även när namnet är
   längre än spalten. */
.listRow__sub > span { min-width:0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis }
/* Tecknet framför typens namn. Något mindre än texten och en aning
   svagare — det ska kännas igen i ögonvrån, inte läsas. */
/* En pixel upp. align-items:center ställer tecknet mitt i textens radbox,
   men bokstävernas synliga massa ligger i radens övre del — mot x-höjden
   och inte mot radmitten. Ett tecken som står geometriskt rätt ser
   därför ut att hänga en aning för lågt bredvid ordet det hör till. */
.listRow__subIco { flex:0 0 auto; width:.875rem; height:.875rem;
  margin-top:-.0625rem;
  stroke:currentColor; fill:none; stroke-width:1.75;
  stroke-linecap:round; stroke-linejoin:round; opacity:.85 }

/* Datumet ytterst i raden, lodrätt centrerat mot båda textraderna. Det
   står där chevronen annars står — och de raderna har ingen chevron, för
   ett datum och en pil på samma plats blir två saker som pekar. */
.listRow__meta--side { align-self:center; padding-left:.75rem }

/* Opublicerat är det enda av de två lägena som kräver något av någon.
   Det bär därför accentfärgen; publicerat är bara ett konstaterande. */
.listRow--pending .listRow__sub,
.pending { color:var(--accent); font-weight:500 }

/* Statusen som en prick där pilen stod.

   Grön betyder ute, orange betyder ändrad sedan sist. Två färger räcker:
   frågan man ställer sig när man tittar på listan är om något väntar, och
   den har bara två svar. */
.listRow__dot { flex:0 0 auto; align-self:center; width:.5rem; height:.5rem;
  border-radius:50%; margin-left:.75rem }
/* var(--ok) och inte hårdkodad grön: pricken ska byta ton med temat,
   precis som .dot--ok gör. */
.listRow__dot--ok { background:var(--ok) }
/* Ändrad sedan publiceringen: accentfärgen, som säger "något väntar".
   Utkast är grått — det väntar inte på någon, det är inte klart. */
.listRow__dot--pending { background:var(--accent) }
.listRow__dot--draft { background:rgba(var(--ink-rgb),.25) }

/* Oläst bär en prick, inte fetstil — fetstil på halva listan gör listan
   svårare att läsa. Pricken står före rubriken nu när ikonrutan är borta;
   det är den enda platsen kvar som pekar på raden utan att stjäla en
   kolumn. */
.listRow--unread .listRow__title::before {
  content:''; display:inline-block; vertical-align:middle;
  width:.4375rem; height:.4375rem; margin:-.125rem .5rem 0 0;
  border-radius:50%; background:var(--accent);
}

/* Handtaget är smalare här än i sektionslistan: raden har ingen kortkant
   att hålla avstånd till, bara sidans egen marginal. Luften till texten
   ligger till höger om det — greppytan ska vara bred, avståndet synligt. */
.listRow .sec__grip { width:1.5rem; margin-left:-.375rem; margin-right:.75rem }

/* Markeringen på en rad som inte flyttas. Den står i handtagets spalt och
   har dess mått, så textkolumnen börjar på samma ställe oavsett om raden
   går att dra eller inte — och frånvaron av handtag säger vad den är. */
.listRow__lead { flex:0 0 auto; display:grid; place-items:center; width:1.5rem;
  margin-left:-.375rem; margin-right:.75rem; color:rgba(var(--ink-rgb),.3) }
.listRow__lead svg { width:1rem; height:1rem; stroke:currentColor; fill:none;
  stroke-width:1.75; stroke-linecap:round; stroke-linejoin:round }

/* En rad som bär ett reglage i stället för att leda vidare. Hela raden är
   en <label>, så tummen träffar rätt utan att någon siktar på reglaget. */
.listRow--switch { gap:.875rem; align-items:center; padding:.875rem 0;
  cursor:pointer }
.listRow__aside { flex:0 0 auto; display:flex; align-items:center }

/* Under dragning: originalet bleknar till en tom plats, den burna kopian
   lyfts med skugga. Samma språk som sektionslistan. */
.listRow--ghost { opacity:.35 }
.listRow--chosen { background:var(--surface) }
.listRow--drag { opacity:1 }

/* ============================ sid- och inläggslistan ================
   En tabell som breddar ut sig när det finns plats och faller ihop när
   det inte gör det. På en telefon: bild, namn och en prick. Vid ett
   tangentbord: samma sak plus status som ord och när sidan senast rördes.

   Bilden står först och är också greppet — man drar i sidan man ser. */
/* Listan i ett ljust kort. Raderna är en tabell och en tabell är en
   sammanhållen sak: utan kort låg de och flöt på sidans egen yta, med
   linjer som började och slutade i ingenting.

   På en telefon finns ingen sådan yta att flyta på — där är listan hela
   bredden, och ett kort runt den hade bara ätit tolv pixlar i kanten. */
.ptableWrap { margin-top:0 }
.ptable { display:flex; flex-direction:column }

/* Raden är en rad i en tabell och inte ett piller. Rundade hörn på varje
   rad gjorde listan till en trave brickor, och linjen mellan två brickor
   är ingen linje utan ett mellanrum. Markeringen vid hovring går därför
   kant till kant, som raden gör. */
.prow {
  /* Vänsterställd text, uttalat. Raden är en <div> på Sidor och Inlägg men
     en <button> under Sociala medier, och en knapp centrerar sin text som
     standard — en kort rubrik hamnade då mitt i spalten medan en lång såg
     vänsterställd ut. Samma rad som .card--row bär av samma skäl. */
  text-align:left;
  /* Bilden står i linje med rubriken och inte mitt i raden.

     Raden är tre våningar hög — namn, adress, status — och en bild
     centrerad i den hamnade mitt emot adressen, alltså mitt emot det
     minst viktiga. Överkant mot överkant binder ihop bilden med det den
     är en bild av. Marginalen är rubrikens halva radavstånd: utan den
     står bildens kant en aning över versalhöjden och ser upphissad ut. */
  display:grid; grid-template-columns:3.25rem minmax(0, 1fr);
  align-items:start; gap:.9375rem;
  /* Ingen egen sidopadding på telefon: kroppen har redan sin, och två
     lager indrag i rad ställde bilden en halv tumme längre in än
     rubriken ovanför. Kortet på skrivbordet lägger tillbaka den. */
  /* Luften mot linjen ovanför och under. Status och datum står till
     höger om namnet nu, på alla bredder, så raden är två våningar och
     inte tre — och då räcker mindre luft. Linjen dämpad: den ska skilja
     raderna åt, inte rita ett rutnät. */
  padding:.625rem 0;
  border-bottom:1px solid color-mix(in srgb, var(--line) 55%, transparent);
}
.prow:last-child { border-bottom:0 }
/* Listan ramas in uppåt också, men bara på telefon.

   Raderna bär var sin underkant, och den sista sin egen borttagna — listan
   slutade alltså i en linje men började i ingenting. Från 38 rem gör
   .ptableWrap:s egen ram jobbet; därunder finns ingen ram, och listan låg
   an mot flikraden ovanför utan att säga var den började. Linjen är samma
   hårstreck som mellan raderna, så listan läses som en och samma sak. */
@media (max-width:1099px) {
  .ptable > .prow:first-child { border-top:1px solid var(--line) }
}

/* Hovringen syntes knappt. Ytan var --surface, som ligger tre steg från
   kortets vita och försvann i det — en markering man måste leta efter är
   ingen markering.

   Nu säger tre saker samma sak på en gång: ytan mörknar mer, rubriken tar
   accentfärgen, och en list i accentfärgen tänds i vänsterkanten. Listen
   är det som gör den läsbar på en halv sekund; den ritas med en inre
   skugga och inte med en kant, så raden inte flyttar sig en pixel när man
   för musen över den. */
.prow { transition:background .12s ease, box-shadow .12s ease }
/* Bara där det finns en muspekare.

   På en telefon finns ingen hovring att visa, men webbläsaren låtsas
   ändå: den sista raden man rörde vid blir kvar i hovertillstånd tills
   man rör något annat. Resultatet var en rad som stod markerad i
   accentfärg långt efter att man lämnat den, och som såg ut att vara vald
   utan att vara det. Ett tryck ska ge kvitto i det ögonblick det sker och
   inte lämna spår efter sig — det gör :active, som står kvar nedanför. */
@media (hover:hover) and (pointer:fine) {
  .prow:hover {
    background:rgba(var(--ink-rgb),.05);
    box-shadow:inset .1875rem 0 0 var(--accent);
  }
  .prow:hover .prow__title { color:var(--accent-to) }
}
.prow:active { background:rgba(var(--ink-rgb),.05) }

/* En rad som inte öppnar något.
   Ändringsloggens rader är poster med en knapp i, inte vägar in i något —
   raden själv går inte att trycka på. Utan det här svarade den på hovring
   och tryck som om den ledde vidare, och en rad som lovar en resa den
   inte kan göra är sämre än en som inte lovar något. */
.prow--static { cursor:default }
.prow--static:hover { background:none; box-shadow:none }
.prow--static:hover .prow__title { color:inherit }
.prow--static:active { background:none }
.prow__title { transition:color .12s ease }

.prow__thumb {
  position:relative; overflow:hidden;
  width:3.25rem; height:3.25rem; border-radius:.625rem;
  margin-top:.4375rem;
  background:var(--surface-2); display:grid; place-items:center;
  flex:0 0 auto;
}
.prow__thumb img { width:100%; height:100%; object-fit:cover; display:block }

/* Initialen där sidorna har sin bild.

   Ett formulärsvar har ingen bild men en människa bakom sig. Bokstaven
   håller samma spalt som tumnageln, så raderna i inkorgen börjar på samma
   punkt som raderna under Hemsida — det var det som saknades.

   Dämpad och inte accentfärgad: rutan säger vem, inte att något är
   viktigt. Det senare säger pricken. */
.prow__thumb--initial {
  color:rgba(var(--ink-rgb),.62);
  font-size:1.25rem; font-weight:600; letter-spacing:-.01em;
  text-transform:uppercase;
}

/* Ett oläst svar. Namnet bär vikten — en färgad prick till hade sagt
   samma sak en gång för mycket i en lista där de flesta raderna är olästa
   när man öppnar den. */
.prow--unread .prow__title { font-weight:600 }
.prow--unread .prow__thumb--initial {
  background:var(--accent); color:#fff;
}
/* Draghandtaget bär egna mått — .sec__grip är 2,5 rem bred och sträcker
   sig i höjd, för det används på sektionsrader där handtaget är en egen
   spalt. Här ÄR handtaget tumnageln, och då ska bilden bestämma formen.

   Startsidan går inte att flytta och saknar därför klassen. Följden var
   att en rad i listan hade en tumnagel med andra mått än de andra —
   fyra punkter bredare och två ur linje. En bild i en lista ska ligga på
   samma ställe i varje rad, annars läser man kanten och inte innehållet. */
.prow__thumb.sec__grip {
  cursor:grab;
  width:3.25rem; height:3.25rem; align-self:start;
}
.prow__thumb.sec__grip:active { cursor:grabbing }
/* Tumnageln i en listrad — sektionslistan.

   Måtten ärvs från .prow__thumb så bilden ligger på exakt samma punkt som
   i sid- och inläggslistan. Skillnaden är omgivningen: .listRow är en
   flexrad utan rutnät, så avståndet till texten och den lodräta
   placeringen sätts här i stället för av spalterna.

   Dubbelklassen behövs för att slå .prow__thumb:s egen toppmarginal, som
   är uträknad för rutnätet den annars sitter i. */
.listRow__thumb.prow__thumb { align-self:center; margin:0 .875rem 0 0 }

.prow__ico { display:grid; place-items:center; color:rgba(var(--ink-rgb),.35) }
.prow__ico svg {
  width:1.125rem; height:1.125rem; stroke:currentColor; fill:none;
  stroke-width:1.75; stroke-linecap:round; stroke-linejoin:round;
}

.prow__open {
  display:grid; grid-template-columns:minmax(0, 1fr);
  align-items:center; gap:.75rem;
  width:100%; min-width:0; padding:.25rem 0; text-align:left; color:inherit;
}
.prow__name { display:flex; flex-direction:column; gap:.125rem; min-width:0 }
.prow__title {
  font-size:.9375rem; font-weight:500; line-height:1.3;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.prow__sub {
  font-size:.8125rem; color:rgba(var(--ink-rgb),.58);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}

/* Status och datum till höger om namnet, under varandra mot högerkanten,
   på alla bredder. De stod på egen rad under namnet på telefonen, och
   raden blev tre våningar hög: namn, adress, status. Namnet får en
   kortare rad och kortas av — en rubrik man ändå känner igen på sina
   första ord — och listan blir en tredjedel kortare. Pricken i
   högerkanten sa samma sak förut, men med en färg man måste lära sig. */
.prow__open { grid-template-columns:minmax(0, 1fr) auto }
.prow__meta {
  display:flex; flex-direction:column; align-items:flex-end; gap:.3125rem;
  margin-top:0; text-align:right;
}
/* Datumet dras in lika mycket som märkets egen stoppning. Utan det låg
   siffrorna längre ut än ordet ovanför dem: två högerställda rader som
   var kantställda men inte såg det ut. */
.prow__when {
  font-size:.75rem; color:rgba(var(--ink-rgb),.55); white-space:nowrap;
  padding-right:.625rem;
}
.prow__dot { display:none }

.prow--ghost { opacity:.35 }
.prow--chosen { background:var(--surface) }
.prow--drag { opacity:1 }

/* Bredden räcker till spalter. Rutnätet står på två ställen och måste
   säga samma sak på båda — huvudet har en tom spalt först, där bilden
   står i raderna. */
/* Vid skrivbordets brytpunkt och inte vid 38 rem.

   Kortet, flikradens platta och rubrikradens knappar bytte skepnad på
   var sin bredd — 30, 38 och 47,5 rem — och mellan dem stod en vy som
   var hälften telefon och hälften skrivbord: kortet runt listan men
   ingen meny, skrivbordets knappar men telefonens rad. Nu finns två
   presentationer och en gräns: under 1100 px är allt telefonens, över
   den allt skrivbordets. En surfplatta får telefonens, bredare. */
@media (min-width:1100px) {
  /* Kortet klipper raderna, så deras linjer går ända ut i kanten och
     följer rundningen i hörnen.

     Inkorgen ritas med samma .prow och samma .ptableWrap som de andra två
     nu, så kortet behöver bara nämnas en gång. .list--table fanns för att
     ge inkorgens listRow-rader samma kort; den byggstenen är utbytt. */
  .ptableWrap {
    background:var(--card); border:1px solid var(--line);
    border-radius:1rem; overflow:hidden;
  }
  /* Raderna bär kortets sidopadding, som .prow gör nedanför. Hårstrecket
     följer med utan att röras: en ram ligger utanför paddingen, så linjen
     går kant till kant i kortet även när innehållet är indraget. Det är
     kortet som håller ihop raderna, inte en indragen linje. */
  /* Bredden räcker till spalter: namnet till vänster, status och datum
     under varandra mot högerkanten. */
  .prow { padding:.5rem 1rem }
  .prow__meta { gap:.4375rem }
  /* Samma indrag för listRow-raderna. Länkar och Inkorgen ritas med
     listRow i samma kort, men bara .prow fick sidopaddingen ovanför —
     där stod ikonrutan dikt an mot kortets vänsterkant och chevronen mot
     den högra, som om raden runnit ut ur sitt kort. Bara i sidled: raden
     bär sin egen höjd i .listRow__open. */
  .ptableWrap .listRow { padding:0 1rem }
}

/* Projektmonogrammet: två bokstäver på en terrakottalutning. Ger listan
   igenkänning utan att kräva att någon laddar upp en bild. */
.mono {
  flex:0 0 auto; display:grid; place-items:center;
  width:2.75rem; height:2.75rem; border-radius:.75rem;
  background:linear-gradient(135deg,var(--accent-from),var(--accent-to));
  color:#fff; font-size:.8125rem; font-weight:600; letter-spacing:.02em;
}
.mono--lg { width:3.5rem; height:3.5rem; border-radius:1rem; font-size:1rem }
.mono--muted { background:var(--surface-2); color:rgba(var(--ink-rgb),.62) }
/* Rutan tar en ikon i stället för initialer när posten inte är en person. */
.mono svg { width:1.125rem; height:1.125rem; stroke:currentColor; fill:none;
  stroke-width:1.75; stroke-linecap:round; stroke-linejoin:round }

/* ============================== lobbyn ============================
   Projektväljaren är appens enda vy utan topplist: det finns ingenting
   att backa till och inget projekt att bära i listen. Rubriken gör
   listens arbete i stället.

   Överst, inte mitt i höjden. Listan stod centrerad — "en kort lista
   mitt på en tom skärm ser avsiktlig ut" — men listan är sällan kort,
   och den byter längd medan man skriver i sökfältet: varje bokstav
   flyttade rubriken och fältet man skrev i uppåt eller nedåt. Ett fält
   som flyttar sig under fingret är värre än en lista som står högt. */
.lobby {
  display:grid;
  align-content:start;
  padding-top:calc(var(--top) + 3rem);
}
/* Innehållet är en kort lista och en rubrik — inte ett arbetsbord. På
   skrivbordet ärver skärmen spaltens 54,5 rem, och en enda projektrad
   utsträckt över den bredden ser mer ödslig ut än rymlig. 30 rem räcker
   för namn, medlemsantal och pil; i telefonramen är skärmen ändå smalare
   och taket märks inte.

   34 och inte 30: raden är samma rad som under Sidor nu — bild, namn,
   adress och ett märke mot högerkanten — och på 30 rem trängdes märket
   in i adressen på de projekt som har en riktig domän. */
.lobby__inner { min-width:0; width:100%; max-width:34rem; margin-inline:auto }
/* Rubriken över inbjudningarna är blockets första rad och bär inget
   avstånd uppåt — luften ovanför är skärmens, inte rubrikens. Under
   blocket ligger i stället ett tydligt avstånd: en inbjudan man inte
   svarat på hör inte till projekten under den. */
#invites-block { margin-bottom:2.25rem }
#invites-block .sectionTitle { margin-top:0 }

/* Rubriken och den enda åtgärd som hör till den, på samma rad. Bryts på
   en smal telefon: två ord i 2 rem plus ett piller ryms inte på 360 px,
   och en avkortad rubrik är sämre än en radbrytning. */
.lobby__head {
  display:flex; align-items:center; justify-content:space-between;
  gap:.875rem 1rem; flex-wrap:wrap; margin-bottom:1.125rem;
}
.lobby__head .h1 { flex:1 1 auto }
.lobby__head .btn { flex:0 0 auto }

/* Sökfältet mellan rubriken och listan.

   Fältet står i index.php och inte i renderProjects, så det behåller fokus
   medan listan ritas om under det — se kommentaren där. Avståndet neråt är
   samma som sidlistans filter bär, så de två skärmarna andas lika. */
#projects-search-wrap { margin:0 0 1rem }

/* Vägen ut, på den plats där Nytt projekt förr stod. Den är dämpad:
   utloggning är sällan det man kom hit för att göra. */
.lobby__foot { margin-top:1.75rem }

/* Inställningarnas huvud: ingen ruta, bara namnet — domänen när den
   finns — och en liten väg ut till sajten. Initialrutan och medlems-
   antalet sa inget som listan under inte redan säger. */
/* Domänen i ett kort, som grupperna under den.

   Den låg förut som en lös rad högst upp på skärmen: samma ord, men utan
   något att höra till. En kantlös rad ovanför en spalt av kort läser sig
   som en rubrik någon glömt forma, inte som en uppgift — och adressen är
   en uppgift, den viktigaste på skärmen.

   Måtten är navlistans radmått, så kortet väger lika mycket som raderna
   under det. */
.settingsHead {
  display:flex; align-items:center; justify-content:space-between; gap:.75rem;
  padding:1rem 1.125rem;
  background:var(--card); border:1px solid var(--line);
  border-radius:var(--radius-card);
}
.settingsHead__name {
  min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  font-size:1rem; font-weight:600; letter-spacing:-.01em;
}

/* Projektets identitetskort.

   Samma kort, men det bär fyra uppgifter i stället för en: namn, adress,
   id och vägen till ett annat projekt. Fyra ryms inte på en rad utan att
   den kortaste klipps först, så kortet är ett block med hårstreck emellan
   — samma streck som skiljer raderna i navlistan under, så de två väger
   lika. */
.settingsHead--project { display:block; padding:0 }
.settingsHead__top {
  display:flex; align-items:center; justify-content:space-between; gap:.75rem;
  padding:1rem 1.125rem;
}
/* Knappen krymper inte och bryts inte: gör den det står "Byt / projekt" i
   två rader så fort projektet har ett långt namn, och det är namnet som
   ska ge vika — det har redan en ellips. */
.settingsHead__top .btn { flex:none; white-space:nowrap }
.settingsHead__id { display:block; min-width:0 }
.settingsHead--project .settingsHead__name { display:block }
.settingsHead__addr {
  display:block; margin-top:.0625rem;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}

/* Id-raden. Monospace därför att id:t läses tecken för tecken när det
   jämförs mot en logg — inte för att det ska se tekniskt ut. */
.settingsHead__meta {
  display:flex; align-items:center; gap:.625rem;
  padding:.5rem .75rem .5rem 1.125rem;
  border-top:1px solid var(--line);
}
.settingsHead__val {
  flex:1; min-width:0;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:.8125rem;
}
.settingsHead__copy {
  flex:none; display:grid; place-items:center; width:2rem; height:2rem;
  border:0; padding:0; border-radius:.625rem;
  background:transparent; color:inherit; cursor:pointer;
}
.settingsHead__copy:hover { background:var(--surface-2) }
.settingsHead__copy svg {
  width:.9375rem; height:.9375rem; stroke:currentColor; fill:none;
  stroke-width:1.75; stroke-linecap:round; stroke-linejoin:round;
}
.settingsHead__actions {
  display:flex; flex-wrap:wrap; gap:.5rem;
  padding:.75rem 1.125rem 1rem;
  border-top:1px solid var(--line);
}

/* ============================== navlista ==========================
   Grupperade rader i ett kort, som en inställningssida ska se ut.
   Hårstrecket ligger mellan raderna, aldrig först eller sist. */
.navlist { background:var(--card); border:1px solid var(--line);
  border-radius:var(--radius-card); overflow:hidden }
.navrow { display:flex; align-items:center; gap:.875rem; width:100%;
  padding:1rem 1.125rem; text-align:left; transition:background .15s }
.navrow + .navrow { border-top:1px solid var(--line) }
.navrow:active { background:var(--surface) }
/* Samma kvitto vid hovring som listraderna ger, se .prow:hover.
   Raderna gör samma sak — de öppnar något — och en pekare som far över
   dem ska få samma svar oavsett vilken lista den råkar stå i. Bara där
   det finns en riktig pekare: på en pekskärm fastnar :hover på det man
   senast rörde och blir en markering som ljuger om vad som är valt. */
@media (hover:hover) and (pointer:fine) {
  .navrow:hover {
    background:rgba(var(--ink-rgb),.05);
    box-shadow:inset .1875rem 0 0 var(--accent);
  }
  .navrow:hover .navrow__label { color:var(--accent-to) }
  .navrow:hover .navrow__chev  { color:rgba(var(--ink-rgb),.5) }
}
.navrow__ico { flex:0 0 auto; width:1.25rem; height:1.25rem; color:rgba(var(--ink-rgb),.55);
  stroke:currentColor; fill:none; stroke-width:1.75; stroke-linecap:round; stroke-linejoin:round }
.navrow__text { flex:1 1 auto; min-width:0 }
.navrow__label { display:block; font-size:.9375rem; font-weight:500; letter-spacing:-.01em;
  transition:color .12s ease }
.navrow__sub { display:block; margin-top:.0625rem; font-size:.8125rem; color:rgba(var(--ink-rgb),.58);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis }
.navrow__chev { flex:0 0 auto; width:1.125rem; height:1.125rem; color:rgba(var(--ink-rgb),.3);
  stroke:currentColor; fill:none; stroke-width:2; stroke-linecap:round; stroke-linejoin:round }
.navrow--danger .navrow__label,
.navrow--danger .navrow__ico { color:var(--danger) }

/* ============================== nyckeltal =========================
   Tre lika breda rutor. Siffrorna är tabulära så att de inte hoppar
   i sidled när värdet ändras. */

/* ============================== statistiken =======================
   Fyra delar som återkommer: periodväljaren, nyckeltalsraden, kurvan och
   korten med topplistor. Samma fyra oavsett om siffrorna är våra egna
   eller Googles — den som lärt sig läsa det övre kortet ska kunna läsa
   det nedre utan att lära om. */

/* Perioden. En rad piller som rullar i sidled på en telefon: åtta val
   ryms inte på 360 pixlar, och en hoprullad meny hade gömt det man byter
   oftast bakom två tryck. */
.periods {
  display:flex; gap:.375rem; margin:0 -1.25rem 1rem; padding:0 1.25rem .25rem;
  overflow-x:auto; scrollbar-width:none;
  /* Radens egen rullning ska stanna i raden. Utan detta kedjas studsen
     vid kanten vidare och känns som att hela sidan rör sig i sidled. */
  overscroll-behavior-x:contain;
}
.periods::-webkit-scrollbar { display:none }

/* Tonade kanter i stället för rak avklippning. Ett halvt piller som bara
   kapas läses som ett fel; en ton säger att raden fortsätter åt det
   hållet. Klasserna sätts av periodFade och släcks när man nått kanten,
   så det sista pillret aldrig står och blänger halvgenomskinligt. */
.periods.fade-r {
  -webkit-mask-image:linear-gradient(90deg, #000 calc(100% - 3rem), transparent);
  mask-image:linear-gradient(90deg, #000 calc(100% - 3rem), transparent);
}
.periods.fade-l {
  -webkit-mask-image:linear-gradient(90deg, transparent, #000 3rem);
  mask-image:linear-gradient(90deg, transparent, #000 3rem);
}
.periods.fade-l.fade-r {
  -webkit-mask-image:linear-gradient(90deg, transparent, #000 3rem, #000 calc(100% - 3rem), transparent);
  mask-image:linear-gradient(90deg, transparent, #000 3rem, #000 calc(100% - 3rem), transparent);
}
/* Ghost-knappar: kontur och ingen fyllning.

   De bar kortfärgen förut, alltså samma yta som allt annat på skärmen,
   och åtta fyllda pillren i rad läste sig som ett block man skulle ta sig
   igenom snarare än som val. Utan fyllning är det konturen som bär dem,
   och då är det bara ETT av dem som har en yta — det valda. */
.period {
  flex:0 0 auto; padding:.4375rem .875rem;
  border:1px solid var(--line); border-radius:var(--radius-pill);
  background:none; color:rgba(var(--ink-rgb),.62);
  font-size:.8125rem; font-weight:500; white-space:nowrap;
  transition:background .15s, border-color .15s, color .15s;
}
.period:hover { color:var(--foreground); border-color:rgba(var(--ink-rgb),.3) }
/* Accenten och inte bläcket. Ett vitt piller i mörkt läge var det ljusaste
   på hela skärmen och drog blicken till periodväljaren i stället för till
   siffrorna den styr. Orange säger "vald" lika tydligt utan att skrika,
   och är samma färg som markerar den aktiva fliken i menyn. */
.period.is-on {
  background:var(--accent); border-color:var(--accent); color:#fff;
}
.period.is-on:hover { color:#fff; border-color:var(--accent) }

/* Nyckeltalen. En rad rutor som delar en enda kant: de hör ihop, och sex
   fristående kort hade sett ut som sex olika saker. */
.kpis {
  display:grid; grid-template-columns:repeat(2, 1fr);
  /* Linjerna mellan rutorna är mellanrummet, inte kanter på rutorna.
     Antalet spalter beror på bredden och antalet tal, och med kanter hade
     varje kombination krävt sin egen regel för vilken ruta som står sist
     i sin rad. Ett mellanrum vet det av sig självt. */
  gap:1px; background:var(--line);
  border:1px solid var(--line); border-radius:1rem;
  overflow:hidden; margin-bottom:1rem;
}
.kpi { background:var(--card); padding:.875rem 1rem }
/* Nio rutor — sex mått och trafikslagens tre andelar i samma kort. På
   telefonens två spalter spänner den nionde hela raden: en tom cell i
   rutnätet hade visat linjefärgen som ett hål. Från 34 rem står de tre
   och tre, se mediefrågan nedan. */
.kpis--9 > .kpi:last-child { grid-column:1 / -1 }
/* Tre tal är en rad och inte två plus en. Två spalter lämnade den fjärde
   cellen tom, och tomrummet lyste upp i linjefärgen som ett hål i kortet.
   Talen är korta — Betyg, Omdömen, Visas på sajten — och ryms i tre
   spalter också på en telefon. */
.kpis--3 { grid-template-columns:repeat(3, 1fr) }
.kpi__l {
  display:block; font-size:.625rem; font-weight:600; text-transform:uppercase;
  letter-spacing:.06em; color:rgba(var(--ink-rgb),.55);
}
.kpi__n {
  display:block; margin-top:.25rem;
  font-size:1.5rem; font-weight:600; letter-spacing:-.03em; line-height:1.1;
  font-variant-numeric:tabular-nums;
}
/* Stjärnorna under betyget, i halvor. Halvan är en tvåfärgad text: den
   vänstra halvan i accent, den högra i den släckta färgen. */
.kpi__x { display:block; margin-top:.375rem }
.stars { display:inline-flex; gap:.125rem; font-size:.9375rem; line-height:1 }
.star { color:rgba(var(--ink-rgb),.22); font-style:normal }
.star--full { color:var(--accent) }
.star--half { background:linear-gradient(90deg, var(--accent) 50%, rgba(var(--ink-rgb),.22) 50%);
  -webkit-background-clip:text; background-clip:text; color:transparent }

/* =============== omdirigeringar ==================
   En rad är två adresser och en pil emellan. Fälten står i ett rutnät med
   pilen som egen spalt: en flexrad hade låtit det längre fältet växa och
   pilen glida ur mitten, och de två adresserna ska väga lika. */
.redirs { display:flex; flex-direction:column; gap:.75rem }
.redir {
  background:var(--card); border:1px solid var(--line);
  border-radius:var(--radius-card); padding:.875rem 1rem;
}
.redir__fields {
  display:grid; grid-template-columns:1fr 1.25rem 1fr;
  align-items:center; gap:.5rem;
}
.redir__f { font-size:.875rem; font-family:ui-monospace,SFMono-Regular,Menlo,monospace }
.redir__arrow {
  width:1.25rem; height:1.25rem; justify-self:center;
  stroke:rgba(var(--ink-rgb),.35); fill:none; stroke-width:2;
  stroke-linecap:round; stroke-linejoin:round;
}
/* På en telefon ryms inte två adresser bredvid varandra utan att båda blir
   för smala att läsa. Under varandra då, med pilen vriden ett kvarts varv
   så den fortsätter peka på det den pekade på. */
@media (max-width:26rem) {
  .redir__fields { grid-template-columns:1fr; gap:.375rem }
  .redir__arrow { transform:rotate(90deg); justify-self:start; margin-left:.5rem }
}
.redir__foot {
  display:flex; align-items:center; justify-content:space-between;
  gap:.75rem; margin-top:.625rem;
}
.redir__tag {
  font-size:.75rem; color:rgba(var(--ink-rgb),.55);
}
.redir__tag--own { color:rgba(var(--ink-rgb),.35) }
.redir__x {
  flex:0 0 auto; display:grid; place-items:center;
  width:2rem; height:2rem; margin:-.25rem -.25rem -.25rem 0;
  border-radius:var(--radius-control);
  color:rgba(var(--ink-rgb),.5); transition:background .15s, color .15s;
}
.redir__x:hover { background:var(--surface-2); color:var(--danger) }
.redir__x svg {
  width:1.0625rem; height:1.0625rem;
  stroke:currentColor; fill:none; stroke-width:1.75;
  stroke-linecap:round; stroke-linejoin:round;
}

/* =============== mätpunkter som går att fråga ==================
   Varje tal är en knapp. Det som öppnar något ska vara en knapp — annars
   hittar varken tangentbordet eller skärmläsaren dit, och muspekaren
   säger ingenting om att det finns mer att veta. */
/* Ingen ruta om rutan.

   Brickorna bär redan var sin ram, och en kortram utanpå dem blev en ram
   om en ram — två streck en hårsmån ifrån varandra som säger samma sak,
   och en stoppning emellan som bara sköt in innehållet. Klassen står kvar
   och nollar kortets utseende i stället för att tas bort ur märkspråket:
   avstånden i statistikspalten hänger på .card + .card, och den kedjan ska
   inte behöva skrivas om för att en ruta försvann. */
.statCard {
  padding:0; background:none; border:0; border-radius:0;
}
/* Undantaget: kortet med de två stora talen och källstapeln.

   Där bar brickorna ramen och stapeln låg utanför den, på sidans yta —
   två rutor och en lös rad om samma sak. Nu bär kortet ramen, brickorna
   släpper sin, och linjen som skiljer talen från stapeln är kortets
   egen: en rad som går tvärs över båda brickorna och binder dem till
   det som står under. */
.statCard--box {
  background:var(--card); border:1px solid var(--line);
  border-radius:var(--radius-control); overflow:hidden;
}
.statCard--box .statTiles { border:0; border-radius:0; border-bottom:1px solid var(--line) }
/* Ingen stapel (för lite data) — då ingen linje under brickorna heller. */
.statCard--box .statTiles:last-child { border-bottom:0 }
.statCard--box .mix { margin:0; padding:1rem 1rem 1.125rem }

/* De två stora: hur många som kom. Linjen emellan är mellanrummet och
   inte kanter, samma grepp som .kpis. */
.statTiles {
  display:grid; grid-template-columns:repeat(2,1fr);
  gap:1px; background:var(--line);
  border:1px solid var(--line); border-radius:var(--radius-control);
  overflow:hidden;
}
.statTile {
  display:flex; flex-direction:column; align-items:flex-start; gap:.25rem;
  width:100%; padding:.875rem 1rem; text-align:left;
  background:var(--card); transition:background .15s;
}
.statTile:hover { background:var(--surface-2) }
.statTile__l {
  display:flex; align-items:center; gap:.3125rem;
  font-size:.625rem; font-weight:600; text-transform:uppercase;
  letter-spacing:.06em; color:rgba(var(--ink-rgb),.55);
}
/* Frågetecknet är blekt tills man rör vid brickan. Det ska gå att hitta,
   inte att läsa — talet är det man kom för. */
.statTile__i {
  width:.8125rem; height:.8125rem; flex:0 0 auto;
  stroke:currentColor; fill:none; stroke-width:2;
  stroke-linecap:round; stroke-linejoin:round;
  opacity:.45; transition:opacity .15s;
}
.statTile:hover .statTile__i { opacity:1 }
.statTile__n {
  font-size:1.25rem; font-weight:600; letter-spacing:-.03em; line-height:1.1;
  font-variant-numeric:tabular-nums;
}
.statTile--big .statTile__n { font-size:1.75rem }

/* Måtten som beskriver hur besöket gick. Rullar i sidled: sex mått i ett
   rutnät blir två rader, och två rader läses som två grupper fast det är
   en. Rullningslisten göms — den hör inte hemma i en app — och kanterna
   tonas i stället, så det syns att raden fortsätter. */
.metricStrip {
  display:flex; gap:1px; background:var(--line);
  border:1px solid var(--line); border-radius:var(--radius-control);
  overflow-x:auto; overflow-y:hidden;
  scrollbar-width:none;
  -webkit-mask-image:linear-gradient(to right, transparent 0, #000 .75rem,
    #000 calc(100% - .75rem), transparent 100%);
  mask-image:linear-gradient(to right, transparent 0, #000 .75rem,
    #000 calc(100% - .75rem), transparent 100%);
}
.metricStrip::-webkit-scrollbar { display:none }
/* width:auto måste stå med. Brickan bär width:100% för rutnätet ovanför,
   och i en rad som rullar betyder hundra procent hela den synliga bredden
   — sex brickor blev sex skärmbredder och bara den första gick att se. */
.metricStrip > .statTile {
  flex:0 0 auto; width:auto; min-width:9.5rem;
  scroll-snap-align:start;
}

/* Från 34 rem blir raden ett rutnät.

   Rullningen finns för telefonen, där sex mått aldrig får plats bredvid
   varandra och två rader hade lästs som två grupper. På en bredare skärm
   är den bara i vägen: sista måttet ligger halvt utanför kanten och den
   tonade masken får det att se ut som ett fel, fast raden bara fortsätter.
   Det som ryms ska synas.

   auto-fit och inte ett fast antal spalter: tre på en läsplatta, sex på ett
   skrivbord, och alltid utan att någon etikett bryts mitt i ordet.
   Masken tas bort med — den tonar kanter som inte längre finns. */
@media (min-width:34rem) {
  .metricStrip {
    /* Tre spalter, inte auto-fit.

       auto-fit räknar ut hur många som ryms och bryr sig inte om hur
       många som finns. Med sex brickor gav det fem spalter runt 890 px
       och fyra runt 720 — alltså fem plus en, och den tomma cellen i
       rutnätet lyste upp i linjefärgen som ett hål i kortet. Det såg
       trasigt ut för att det var trasigt.

       Talet måste dela sex jämnt: två, tre eller sex. Sex i bredd har
       aldrig fungerat — den längsta etiketten, AVVISNINGSFREKVENS,
       behöver 179 px och statistiken bor i en spalt på dryga femtio rem,
       alltså under 1074 px som sex sådana kräver. Kvar står tre, och tre
       plus tre är en jämn figur i stället för en trasig. */
    display:grid; grid-template-columns:repeat(3, 1fr);
    /* hidden och inte visible. Rutnätets brickor har fyrkantiga hörn, och
       utan klippning stack de ut genom ramens rundning — kvar i hörnet
       blev en ljus kil i linjefärgen. Rullningen som overflow-x fanns för
       är borta här ändå; det är ett rutnät nu. */
    overflow:hidden;
    -webkit-mask-image:none; mask-image:none;
  }
  .metricStrip > .statTile { width:auto; min-width:0 }
}

/* =============== trafikslagens fördelning ==================
   En stapel i stället för tre lösa procenttal: jämförelsen görs av ögat
   och inte i huvudet. */
/* Luft mellan talen och stapeln. De är två svar på samma fråga — hur
   många, och varifrån — och behöver skiljas åt utan att lämna kortet. */
.mix { margin-top:1.25rem }
.mixBar {
  display:flex; height:.625rem; gap:2px;
  border-radius:var(--radius-pill); overflow:hidden;
  background:rgba(var(--ink-rgb),.08);
}
.mixBar__s { display:block; min-width:2px; transition:width .3s }
/* Två och två, inte en radbrytning där det råkar ta slut.

   Fyra slag på en flexrad blev tre på första raden och ett på andra,
   olika beroende på hur långa procenttalen var — en uppställning som
   ändrar form med sitt eget innehåll läses som stökig även när den inte
   är det. Ett rutnät ger samma bild varje gång, och den fjärde ligger
   under den andra i stället för i marginalen. */
.mixKeys {
  display:grid; grid-template-columns:1fr 1fr;
  gap:.5rem .75rem; margin-top:.875rem;
}
/* Etiketten får krympa, talet aldrig: "AI-tjänster" med tre punkter är
   fortfarande läsbart, "4 %" med tre punkter är ingenting. */
.mixKey { min-width:0 }
.mixKey__l { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
.mixKey__v { flex:0 0 auto }
.mixKey {
  display:flex; align-items:center; gap:.375rem;
  padding:.1875rem .375rem; margin:-.1875rem -.375rem;
  border-radius:var(--radius-control); background:none;
  transition:background .15s;
}
.mixKey:hover { background:var(--surface-2) }
.mixKey__d { width:.5rem; height:.5rem; border-radius:50%; flex:0 0 auto }
.mixKey__l { font-size:.8125rem; color:rgba(var(--ink-rgb),.66) }
.mixKey__v { font-size:.8125rem; font-weight:600; font-variant-numeric:tabular-nums }

/* ---------- omdömena i statistiken ---------- */
.revList { display:flex; flex-direction:column; gap:.625rem; margin-top:1rem }
.revRow { display:flex; flex-direction:column; gap:.375rem }
/* Det som inte kommer med på sajten står dämpat. Man ska se att det finns
   utan att tro att det syns för besökaren. */
.revRow--dim { opacity:.55 }
.revRow__head { display:flex; align-items:baseline; gap:.625rem; flex-wrap:wrap }
.revRow__who { font-size:.875rem; font-weight:600 }
.revRow__stars { font-size:.8125rem; color:#f5a623; letter-spacing:.05em }
.revRow__when { margin-left:auto; font-size:.75rem; color:rgba(var(--ink-rgb),.5) }
.revRow__text { font-size:.875rem; line-height:1.55; color:rgba(var(--ink-rgb),.75) }

/* =============== arket för en mätpunkt ================== */
.statInfo__big {
  font-size:2rem; font-weight:600; letter-spacing:-.03em; line-height:1.1;
  font-variant-numeric:tabular-nums; margin-bottom:1.25rem;
}
.statInfo__h {
  font-size:.625rem; font-weight:600; text-transform:uppercase;
  letter-spacing:.06em; color:rgba(var(--ink-rgb),.55); margin-bottom:.375rem;
}
.statInfo__p { font-size:.875rem; line-height:1.6; color:rgba(var(--ink-rgb),.7) }
.statInfo__rows { margin-top:1.25rem; border-top:1px solid var(--line) }
.statInfo__row {
  display:flex; align-items:center; justify-content:space-between; gap:1rem;
  padding:.625rem 0; font-size:.875rem;
  border-bottom:1px solid var(--line);
}
.statInfo__row > span:first-child { color:rgba(var(--ink-rgb),.66) }
.statInfo__row > span:last-child { font-weight:600; font-variant-numeric:tabular-nums }

/* =============== Live ==================
   Pricken lyser grönt och pulserar bara när någon faktiskt är inne.

   En prick som rör sig hela tiden slutar betyda något — den blir en del av
   möblemanget, och då säger den lika lite vid noll som vid tolv. Rörelsen
   är reserverad för det den ska rapportera: att det står någon på sajten
   just nu. Vid noll ligger pricken still och dämpad, som en släckt lampa.

   Grönt och inte accentfärgat: det här är inte en av sajtens egna färger
   utan en statuslampa, och grönt är det enda den färgen betyder. */
.liveDot {
  display:inline-block; flex:0 0 auto;
  width:.4375rem; height:.4375rem; border-radius:50%;
  background:rgba(var(--ink-rgb),.3);
}
.liveDot--on { background:var(--ok) }
.liveDot--on.liveDot--beat { animation:livePulse 2s ease-in-out infinite }
.seg--tabs .liveDot { margin-right:.375rem; vertical-align:.05em }
.liveNow { display:flex; align-items:baseline; gap:.5rem; flex-wrap:wrap }
.liveNow .liveDot { align-self:center }
.liveNow__n {
  font-size:2.25rem; font-weight:600; letter-spacing:-.03em; line-height:1;
  font-variant-numeric:tabular-nums;
}
.liveNow__l { font-size:.875rem; color:rgba(var(--ink-rgb),.62) }
@keyframes livePulse { 0%,100% { opacity:1 } 50% { opacity:.25 } }
@media (prefers-reduced-motion:reduce) { .liveDot { animation:none } }

/* =============== märkestecken i listorna ================== */
.brandIco {
  width:1.125rem; height:1.125rem;
  stroke:currentColor; fill:none; stroke-width:1.6;
  stroke-linecap:round; stroke-linejoin:round;
}

/* Växeln över kurvan. Står till vänster där kurvan börjar, inte till
   höger där teckenförklaringen stod: det är ett val man gör innan man
   läser, inte en förklaring man söker efteråt. */
.chartHead { display:flex; align-items:center; margin-bottom:.5rem }

/* Kurvan. Höjden är fast och bredden hela kortet: en graf som ändrar
   höjd när talen ändras går inte att jämföra med sig själv. */
.chartCard { padding:1rem .875rem .75rem }
.chart { position:relative }
.chart__legend {
  display:flex; align-items:center; justify-content:flex-end;
  flex-wrap:wrap; gap:.25rem 1rem; margin-bottom:.5rem;
}
.chart__note { flex:1 1 12rem; font-size:.75rem; color:rgba(var(--ink-rgb),.55) }
/* Prickens kärna i kortets färg, så punkten läses som en markör på linjen
   och inte som en klump i den. */
.chart__dotIn { stroke:var(--card) }
.chart__key {
  display:inline-flex; align-items:center; gap:.375rem;
  font-size:.75rem; color:rgba(var(--ink-rgb),.62);
}
.chart__key i { width:.75rem; height:2px; border-radius:2px }
/* overflow:hidden och inte visible. Rutan är tusen enheter bred och skalas
   ned; punkternas ram och axeltexten kunde rita några enheter utanför, och
   det räckte för att ge vyn en rullning i sidled. */
.chart__svg { display:block; width:100%; height:11rem; overflow:hidden }
.chart__grid { stroke:var(--line); stroke-width:1; vector-effect:non-scaling-stroke }
.chart__ax {
  /* Storleken är i rutans egna enheter och inte i pixlar: rutan är tusen
     bred och skalas ned till spaltens bredd, så en elva blir en åtta. */
  font-size:16px; fill:rgba(var(--ink-rgb),.35);
  /* Texten ska inte sträckas med rutan. preserveAspectRatio:none skalar
     allt, och en siffra som blivit dubbelt så bred som hög läses som en
     bugg. */
  transform:none; vector-effect:non-scaling-stroke;
}
.chart__cursor { stroke:rgba(var(--ink-rgb),.25); stroke-width:1; vector-effect:non-scaling-stroke }
.chart__dot { stroke:var(--card); stroke-width:2 }
.chart__x {
  display:flex; justify-content:space-between; margin-top:.375rem;
  font-size:.6875rem; color:rgba(var(--ink-rgb),.5);
}
.chart__empty {
  padding:2rem 1rem; text-align:center;
  font-size:.875rem; color:rgba(var(--ink-rgb),.55);
}

/* Rutan som följer pekaren. Den ligger ovanpå kurvan och tar aldrig emot
   ett klick: den som pekar på en dag ska kunna fortsätta peka. */
.chart__tip {
  position:absolute; top:2.25rem; pointer-events:none;
  padding:.625rem .75rem; border-radius:.75rem;
  background:var(--card); border:1px solid var(--line);
  box-shadow:0 .5rem 1.5rem rgba(10,10,10,.12);
  font-size:.75rem; white-space:nowrap; z-index:2;
}
.chart__tip[hidden] { display:none }
.chart__tipD { font-weight:600; margin-bottom:.25rem }
.chart__tipR { display:flex; align-items:center; gap:.375rem; color:rgba(var(--ink-rgb),.66) }
.chart__tipR i { width:.625rem; height:2px; border-radius:2px }

/* Korten med topplistor. Ett per fråga, två i bredd när det finns plats.

   Korten sträcks inte till varandras höjd: en kort lista i ett långt kort
   lämnade en tom botten som såg ut att vänta på något. start låter varje
   kort sluta där innehållet slutar. */
.statGrid { display:grid; gap:.75rem; margin-bottom:.75rem; align-items:start }
/* Kortens radavstånd gäller staplade kort, inte rutnätets. I rutnätet är
   gapet avståndet, och en marginal ovanpå sköt ned det högra kortet en
   halv rad mot det vänstra. */
.statGrid > .card + .card,
#stats-body .statGrid > .card + .card { margin-top:0 }

/* En och samma luft mellan statistikens alla block.

   1,75 rem och inte 1,25. Varje block är ett kort med egen ram, och när
   korten står tätare än sin egen innerkant läses de som en enda tabell med
   linjer i — man ser en vägg av rutor i stället för fem svar på fem frågor.

   Listan innehåller .statGrid med flit. Rutnätet med Sidor och Trafik är
   ingen .card och föll därför utanför card + card: nyckeltalen ovanför låg
   kant i kant med det, och det var det enda stället på hela skärmen utan
   luft alls.

   Avgränsat till statistikvyn: kortens 0,75 rem gäller kvar i resten av
   appen, där korten står i grupper och inte i en stapel. */
#stats-body .kpis { margin-bottom:1.75rem }
#stats-body .statGrid { gap:1.75rem; margin-bottom:1.75rem }
#stats-body .card + .card,
#stats-body .card + .statGrid,
#stats-body .card + .placeholder,
#stats-body .card + .empty,
#stats-body .statNote + .card,
#stats-body .statNote + .statGrid,
#stats-body .kpis + .statGrid { margin-top:1.75rem }
#stats-body .scStatus { margin-bottom:1.75rem }
/* Ett kort får aldrig göra spalten bredare. Rutnätsbarn har min-width:auto
   och ärver sitt bredaste innehåll — en lång hänvisaradress eller ett
   korthuvud med tre flikar räckte för att ge hela vyn en rullning i
   sidled. Periodraden undantas med flit: den är kant till kant och rullar
   inom sig själv. */
.statGrid > *, .kpis > * { min-width:0 }
.statList, .chartCard { max-width:100% }
.statList { padding:1rem .875rem .5rem }
.statList__head {
  display:flex; align-items:center; justify-content:space-between;
  gap:.75rem; flex-wrap:wrap; margin-bottom:.75rem;
}
.statList__title { font-size:1rem; font-weight:600; letter-spacing:-.01em }
.statList__rows { display:flex; flex-direction:column }
.statList__empty {
  padding:1.5rem .25rem; font-size:.8125rem; color:rgba(var(--ink-rgb),.55);
}

/* Växeln i kortets huvud. Samma tre lägen alltid, det valda ifyllt.

   Egen klass och inte .seg: den stora växeln bär kortyta, ram och
   fyrtio pixlars höga knappar, och den här ska väga som en etikett. */
.segMini { display:flex; gap:.125rem; flex-wrap:wrap }
.segMini__b {
  padding:.3125rem .625rem; border-radius:var(--radius-pill);
  font-size:.75rem; font-weight:500; color:rgba(var(--ink-rgb),.55);
}
.segMini__b:hover { color:var(--foreground) }
.segMini__b.is-on { background:var(--primary); color:var(--on-primary) }

/* Två lägen av samma val, inte två knappar. Rutan håller ihop dem och gör
   att den påslagna läses som "den här av de två" i stället för som en
   ensam markerad knapp. Används av skärmstorleksfliken, se type 'break'. */
/* Bandet i botten på förhandsgranskningen när man tittar på ett tema.

   Fast i underkanten och ovanpå ramen: det man tittar på fortsätter under
   det, och valet ska vara inom räckhåll utan att man rullar tillbaka.
   Skuggan och inte en kant — en kant hade ritat en till linje i en vy som
   redan är full av kundens egna. */
.previewTema {
  position:absolute; left:0; right:0; bottom:0; z-index:3;
  display:flex; align-items:center; justify-content:space-between; gap:1rem;
  padding:.875rem 1rem calc(.875rem + env(safe-area-inset-bottom));
  background:var(--card); color:var(--foreground);
  box-shadow:0 -1px 0 0 var(--line), 0 -12px 32px -12px rgba(0,0,0,.35);
}
.previewTema__text { display:flex; flex-direction:column; min-width:0 }
.previewTema__title { font-size:.9375rem; font-weight:600; letter-spacing:-.01em }
.previewTema__sub { margin-top:.125rem; font-size:.75rem; line-height:1.4;
  color:rgba(var(--ink-rgb),.55) }
.previewTema .btn { width:auto; flex:0 0 auto }

.segMini--boxed { display:inline-flex; flex-wrap:nowrap; gap:.125rem;
  padding:.1875rem; border-radius:var(--radius-pill);
  background:rgba(var(--ink-rgb),.06) }

/* En rad i en topplista. Stapeln ligger bakom texten och inte bredvid:
   en egen spalt hade tagit plats från namnen, som är det man läser, och
   proportionen syns lika bra som en ton bakom orden. */
/* Raderna är rena rader. Tonen som förr låg bakom varje rad i proportion
   till talet (--p) är borta — den gjorde listan till ett mönster, och
   talet i högerkanten säger redan hur raderna förhåller sig. */
.barRow {
  display:flex; align-items:center; gap:.75rem;
  padding:.5rem .625rem; border-radius:.625rem;
  font-size:.875rem;
}
.barRow__t {
  display:flex; align-items:center; gap:.625rem;
  min-width:0; flex:1 1 auto;
}
.barRow__n { overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
.barRow__i {
  flex:0 0 auto; display:grid; place-items:center;
  width:1.125rem; height:1.125rem; color:rgba(var(--ink-rgb),.5);
}
.barRow__i svg {
  width:1rem; height:1rem; stroke:currentColor; fill:none;
  stroke-width:1.75; stroke-linecap:round; stroke-linejoin:round;
}
.barRow__v {
  flex:0 0 auto;
  font-variant-numeric:tabular-nums; color:rgba(var(--ink-rgb),.66);
}
.flag { font-size:1rem; line-height:1 }
/* Flaggan som bild: fast bredd, rundade hörn och en hårfin ram så att
   vita fält (Japan, Finland) inte flyter ihop med kortet. */
.flagImg {
  display:block; width:1.125rem; height:auto; border-radius:2px;
  box-shadow:0 0 0 1px rgba(var(--ink-rgb),.08);
}

/* Statusraden för Search Console. Pricken säger att kopplingen lever;
   texten säger vad den gäller och när den senast hämtade. */
/* Anmärkningen under en kurva. Den förklarar vad man just tittat på och
   hör därför ihop med kurvan ovanför — men den satt med en negativ
   marginal klistrad i kortets underkant, och en förklaring som rör vid
   det den förklarar läses som en del av det. */
.statNote {
  margin:.75rem 0 1.5rem; padding:0 .125rem;
  font-size:.8125rem; line-height:1.6; color:rgba(var(--ink-rgb),.58);
}

/* Statusen står sist i vyn och bär sitt avstånd uppåt. */
.scStatus { margin-top:1.25rem }
.dot {
  display:inline-block; width:.5rem; height:.5rem; border-radius:50%;
  margin-right:.375rem; vertical-align:middle;
}
.dot--ok { background:var(--ok) }

/* Antalet spalter följer antalet tal och inte bara bredden.

   Med auto-fit blev sex nyckeltal sex spalter på 137 pixlar var, och
   "Avvisningsfrekvens" bröt mitt i ordet. Rutan ska vara bredare än sin
   längsta etikett, och hur bred det är vet den som skrev etiketterna. */
@media (min-width:34rem) {
  .kpis--4 { grid-template-columns:repeat(4, 1fr) }
  .kpis--6 { grid-template-columns:repeat(3, 1fr) }
  .kpis--9 { grid-template-columns:repeat(3, 1fr) }
  .kpis--9 > .kpi:last-child { grid-column:auto }
}
@media (min-width:44rem) {
  .statGrid { grid-template-columns:1fr 1fr }
  .chart__svg { height:14rem }
}
/* Fler än så blir de inte. Statistiken bor i en spalt på dryga femtio rem
   oavsett hur brett fönstret är, och en mediefråga mäter fönstret och inte
   spalten — sex rutor på en rad blev därför sex smala rutor med brutna
   etiketter så fort skärmen var stor nog att lura regeln. */

/* ============================== avatarrad =========================
   Överlappande monogram. Kanten i kortets färg klipper ut grannen
   så raden läses som staplade brickor. */

/* ============================== draghandtag =======================
   Bara handtaget startar en flytt — annars skulle varje försök att rulla
   listan på en telefon börja sortera i stället. */
.sec__grip { flex:0 0 auto; display:grid; place-items:center; width:2.5rem;
  align-self:stretch; color:rgba(var(--ink-rgb),.3); cursor:grab; touch-action:none }
.sec__grip:active { cursor:grabbing }
.sec__grip svg { width:1.25rem; height:1.25rem; fill:currentColor; stroke:none }

/* Etiketten över ett nyckeltal. Samma form som listornas småetiketter. */

/* ============================== notruta ===========================
   Förklarande block som inte är ett formulär och inte ett tomt läge. */
.note { margin-top:1.25rem; padding:1.125rem 1.25rem; background:var(--surface);
  border-radius:var(--radius-card) }
.note__title { font-size:.875rem; font-weight:600; letter-spacing:-.01em }

.subTitle { margin:1.5rem 0 .25rem; font-size:.6875rem; font-weight:600;
  text-transform:uppercase; letter-spacing:.08em; color:rgba(var(--ink-rgb),.55) }

/* En avdelning i en skärm, inte en fältetikett. Versaler i 11 px läses som
   ännu en etikett bland fälten; den här ska säga att ett nytt område
   börjar, och gör det med storlek i stället för med spärrad text. */
.groupTitle { margin:2.25rem 0 .375rem; font-size:1.0625rem; font-weight:600;
  letter-spacing:-.015em; color:var(--foreground) }

/* ============================== bildruta ==========================
   Fast bildförhållande så listan inte hoppar när en bild byts. */
/* Ljus bakgrund, inte grå. En grå ruta läser som en inaktiv yta; en vit
   läser som en plats där något ska ligga.

   Full bredd. Smalare provades och gjorde bara raden ojämn mot fälten
   under, utan att lugna ned något. */
.imgBox { position:relative; display:block; width:100%; aspect-ratio:16 / 9; overflow:hidden;
  border:1px dashed var(--subtle); border-radius:var(--radius-control);
  background:var(--card); transition:border-color .2s }
.imgBox:active { border-color:var(--accent) }

/* Papperskorgen i bildens hörn.

   Den satt som en textrad under bilden förut och tog en egen höjd i
   formuläret, även när man bara ville byta bild. Mörk platta med vit ikon,
   för den ligger ovanpå ett foto som kan vara vilket som helst. */
.imgWrap { position:relative }
.imgWrap__x { position:absolute; top:.625rem; right:.625rem;
  display:grid; place-items:center; width:2.25rem; height:2.25rem;
  border:0; border-radius:50%; padding:0; cursor:pointer;
  background:rgba(10,10,10,.55); color:#fff;
  backdrop-filter:blur(4px); transition:background .15s }
.imgWrap__x:hover { background:rgba(10,10,10,.78) }
/* Alt-knappen i motsatt hörn: samma runda bricka som papperskorgen, men
   med ord i stället för tecken. Det finns ingen symbol för alt-text som
   någon känner igen utan förklaring, och tre versaler är kortare än
   förklaringen.

   Nedre högra hörnet och inte övre: den som just laddat upp en bild
   siktar på papperskorgen om något gick fel, och två runda knappar
   bredvid varandra i samma hörn hade blivit ett val man måste läsa.

   Ifylld text lyser upp knappen. Då syns det i en lista med tolv kort
   vilka bilder som saknar sin beskrivning, utan att man öppnar dem. */
/* Kontur och inte fyllning i viloläget.

   Den bar samma mörka platta som papperskorgen och vägde därför lika
   mycket som den — fast den ena raderar en bild och den andra öppnar en
   textruta. Med kontur säger knappen att den finns utan att ta plats i
   bilden, och när alt-texten väl är ifylld fylls den med accenten: då är
   det ett besked och inte ett erbjudande.

   Ordet är litet med flit. Tre versaler i .5625 rem läses ändå, och en
   knapp som ligger ovanpå någons fotografi ska vara det minsta som går
   att träffa — inte det största som får plats. */
.imgWrap__alt { position:absolute; right:.625rem; bottom:.625rem;
  display:grid; place-items:center; width:2.25rem; height:2.25rem;
  border:0; border-radius:50%; padding:0; cursor:pointer;
  background:rgba(10,10,10,.28); color:#fff;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.5);
  /* Longhands och inte font-genvägen. "font:600 .5625rem/1 inherit" ser
     riktigt ut men är ogiltig — inherit får bara stå som HELA värdet på en
     deklaration, aldrig som en del av en genväg — så webbläsaren kastade
     hela raden och knappen ritade sitt ord i knappens normalgrad. */
  font-family:inherit; font-weight:600; font-size:.5625rem; line-height:1;
  letter-spacing:.08em;
  backdrop-filter:blur(4px);
  transition:background .15s, color .15s, box-shadow .15s }
.imgWrap__alt:hover {
  background:rgba(10,10,10,.6); box-shadow:inset 0 0 0 1px rgba(255,255,255,.8) }

/* Namnet i bildrutans övre vänstra hörn — Före, Efter.

   Samma bricka som papperskorgen och ALT, fast med ord och i motsatt
   hörn: det är en uppgift om rutan och inte en knapp, så den ska inte
   ligga där handen siktar. Mörk platta med vit text fungerar mot båda
   underlagen — en uppladdad bild av vilken färg som helst, och den tomma
   rutans ljusa yta.

   pointer-events:none, annars vore den ett hål i rutan man trycker på för
   att välja bild. */
/* Uppräkning av korta ord: tjänsterna som lästs av bokningssidan. Inte
   knappar — de går inte att trycka på — utan en lista i radform, för det
   är formen ögat läser snabbast när posterna är två ord långa. */
.chips { display:flex; flex-wrap:wrap; gap:.375rem }

.imgBox__tag {
  position:absolute; top:.625rem; left:.625rem; z-index:1;
  padding:.1875rem .5rem; border-radius:var(--radius-pill);
  background:rgba(10,10,10,.55); color:#fff;
  backdrop-filter:blur(4px); pointer-events:none;
  /* Samma ogiltiga genväg som på ALT-knappen ovan, samma rättelse. */
  font-family:inherit; font-weight:600; font-size:.625rem; line-height:1.4;
  letter-spacing:.06em; text-transform:uppercase;
}
/* Ifylld alt-text fyller knappen. Konturen bort — en fylld yta med en
   ring runt sig är två markeringar för ett tillstånd. */
.imgWrap__alt.is-set { background:var(--accent); color:#fff; box-shadow:none }
.imgWrap__alt.is-set:hover { background:var(--accent-to); box-shadow:none }
.imgWrap__x svg { width:1rem; height:1rem; stroke:currentColor; fill:none;
  stroke-width:1.75; stroke-linecap:round; stroke-linejoin:round }
.imgBox img { position:absolute; inset:0; width:100%; height:100%; object-fit:cover }
.imgBox__hint { position:relative; display:grid; place-items:center; height:100% }
/* En ikon i stället för en uppmaning. En streckad ruta med en bildsymbol
   säger vad den är åt alla som sett en sådan förut, och behöver inte
   översättas. */
.imgBox__ico { width:2rem; height:2rem; color:rgba(var(--ink-rgb),.28);
  stroke:currentColor; fill:none; stroke-width:1.75;
  stroke-linecap:round; stroke-linejoin:round }
.imgBox.has-image { border-style:solid; border-color:var(--line) }

/* Logotypens ruta. Hel bild, inte beskuren, och på den färg logotypen
   kommer att ligga på ute på sajten — annars visar rutan en svart
   logotyp på ett mörkt kort och man ser ingenting alls. Lägre än 16:9:
   en logotyp är en rad, inte ett foto. */
.imgBox--logo { aspect-ratio:16 / 6; background:var(--logo-bg, var(--card)) }
.imgBox--logo img { object-fit:contain; padding:1.25rem }
.imgBox--logo.has-image { border-color:var(--line) }
.imgBox.has-image .imgBox__hint { display:none }

/* Favikonens ruta är en kvadrat och inte en bredbild: den visar en ikon,
   och en 16:9-ruta hade sagt att den vill ha ett foto. Bilden ryms hel i
   rutan (contain) — beskuren hade den visat något annat än det som
   hamnar i fliken. Rutig botten under, så en genomskinlig ikon syns som
   genomskinlig i stället för som en tom ruta. */
.imgBox--icon { width:7rem; aspect-ratio:1 / 1; flex:0 0 auto; cursor:pointer }
.imgBox--icon img { object-fit:contain; padding:.375rem }
.favRow { display:flex; align-items:center; gap:1rem; flex-wrap:wrap }
.favRow__acts { display:flex; flex-direction:column; gap:.5rem; align-items:flex-start }
.favRow__acts .btn { width:auto }

/* Alt-texten står tätt under bilden den beskriver, med en egen liten
   etikett: den är ett fält i fältet och ska inte se ut som nästa fråga i
   formuläret. */
.ctrl--sm { min-height:2.5rem; padding:.5rem .875rem; font-size:.875rem }
/* Växeln under alt-texten hör till samma ruta och behöver ingen egen
   överkant — fältet ovanför är dess rubrik.

   Egen återställning av etikettstilen: switchRow ÄR en label, och som
   direkt barn till .field ärvde den fältetikettens versaler. Raden
   "SKRIVER UT ALT-TEXTEN UNDER BILDEN PÅ SIDAN" är inte en etikett utan
   en mening, och en mening i versaler läses långsammare och låter arg. */
.field > label.switchRow--tight {
  padding:.625rem 0 0; border-top:0;
  font-size:inherit; font-weight:inherit; text-transform:none;
  letter-spacing:normal; color:inherit;
}

/* Förloppsbandet visas bara under uppladdning. */
.imgBox__bar { position:absolute; left:0; right:0; bottom:0; height:.25rem;
  background:rgba(var(--ink-rgb),.12); display:none }
.imgBox.is-uploading .imgBox__bar { display:block }
.imgBox.is-uploading .imgBox__hint { display:grid }
.imgBox__bar i { display:block; height:100%; width:0; background:var(--accent);
  transition:width .2s }

/* Bildgenerering. Till skillnad från en uppladdning finns ingen andel att
   visa — modellen svarar när den svarar — så bandet löper i stället för att
   fyllas, och rutan får ett skimmer som säger att något är på väg.

   Läget ligger på .imgBox och inte på ett eget element, för rutan kan redan
   ha en bild i sig: byter man ut en bild ska den gamla ligga kvar och dämpas
   tills den nya är klar. */
.imgBox.is-generating { border-style:solid; border-color:var(--line) }
.imgBox.is-generating img { opacity:.25; filter:saturate(.4) }
.imgBox.is-generating::before {
  content:''; position:absolute; inset:0; z-index:1;
  background:linear-gradient(100deg,
    transparent 20%,
    rgba(var(--ink-rgb),.07) 45%,
    transparent 70%);
  background-size:220% 100%;
  animation:imgSkimmer 1.5s linear infinite }
.imgBox.is-generating .imgBox__hint { display:grid; position:relative; z-index:2 }
.imgBox.is-generating .imgBox__ico { display:none }
.imgBox.is-generating .imgBox__hint::after {
  content:'Skapar bilden …'; font-size:.8125rem; font-weight:500;
  color:rgba(var(--ink-rgb),.62) }
.imgBox.is-generating .imgBox__bar { display:block; z-index:2 }
.imgBox.is-generating .imgBox__bar i { width:38%; animation:imgLoper 1.2s ease-in-out infinite }

@keyframes imgSkimmer { from { background-position:120% 0 } to { background-position:-120% 0 } }
@keyframes imgLoper   { from { margin-left:-38% } to { margin-left:100% } }

/* Rörelsen är dekor. Beskedet är texten, och den står kvar. */
@media (prefers-reduced-motion:reduce) {
  .imgBox.is-generating::before { animation:none }
  .imgBox.is-generating .imgBox__bar i { animation:none; width:100%; opacity:.4 }
}

/* ============================== segment ===========================
   Två eller tre lägen som utesluter varandra. */
/* Ljus botten under flikarna. Den aktiva fliken är mörk, och kontrasten
   blir bara tydlig om det den ligger på är ljusare än sidan omkring. */
.seg { display:flex; gap:.25rem; padding:.25rem; background:var(--card);
  border:1px solid var(--line); border-radius:var(--radius-control) }
.seg button { flex:1 1 0; height:2.5rem; border-radius:.625rem;
  font-size:.875rem; font-weight:500; color:rgba(var(--ink-rgb),.66); transition:background .15s, color .15s }
/* Vald flik: en fylld platta som vänder med temat. Nästan vit med svart
   text i mörkt läge, svart med vit text i ljust.

   Den har gått ett varv. Först --primary, sedan accenten, sedan en dämpad
   ton av bläcket — den sista med skälet att en flik inte är en åtgärd och
   inte ska väga som en. Tonen var för svag för att bära markeringen
   ensam: på en armlängds håll gick det inte att se vilken flik man stod
   på, och telefonen hade redan en egen regel med --primary just därför.
   Två utseenden för samma kontroll är sämre än ett som väger lite för
   mycket, och nu är det ETT: samma platta på telefon som på skrivbord.

   Skuggan är fortfarande borta. Plattan bär sig själv. */
.seg button.is-on {
  background:var(--primary); color:var(--on-primary); font-weight:600;
}
/* Räknaren i en flik. Samma cirkel som brickorna ovanför skrivfältet, av
   samma skäl: ett tal som ändrar sig bär färg som yta, resten inte.

   Marginalen och inte radens gap: flikens innehåll är centrerat, och ett
   gap hade flyttat ordet åt vänster också när siffran saknas. */
.seg__n {
  display:inline-grid; place-items:center; vertical-align:.05em;
  margin-left:.4375rem;
  min-width:1.125rem; height:1.125rem; padding:0 .25rem;
  border-radius:999px; background:var(--accent); color:#fff;
  font-size:.6875rem; font-weight:700; font-style:normal;
  font-variant-numeric:tabular-nums;
}
/* På den valda fliken står den mot en ljusare platta och behöver ingen
   egen ring — men den ska inte heller ärva flikens fetstil. */
.seg button.is-on .seg__n { font-weight:700 }

/* Hovringen bara där det finns en pekare. På en pekskärm fastnar :hover
   på det man senast rörde och blir en markering som ljuger om vad som är
   valt. Halva den valda plattans styrka: den ska antyda att fliken går att
   trycka på, inte se vald ut. */
@media (hover:hover) and (pointer:fine) {
  .seg button:not(.is-on):hover {
    background:rgba(var(--ink-rgb),.05); color:var(--foreground);
  }
}

/* Samma kontroll som flikar högst upp i en vy. Lägre än en knapp: den är
   en väg mellan vyer och inte en åtgärd, och ska inte väga som en. */
.seg--tabs { padding:.3125rem }
.seg--tabs button { height:2.25rem; font-size:.875rem }
/* Sidflikens andra ord. Samma flik, två namn: Förhandsgranska på
   telefonen — där fliken är vägen till att se sidan — och Assistent på
   skrivbordet, där hemsidan redan står bredvid. Skrivbordsblocket
   byter, se seg--ptab där. */
.seg--ptab .ptabWord--desk { display:none }

/* ---- rubrikraden i sid-, inläggs- och medialistan ----
   Sök och åtgärd står bredvid rubriken och inte i en egen ruta ovanför
   listan. De hör till listan under: man söker i den och lägger till i
   den, och en kontroll med eget skal emellan läser sig som ett tredje
   ting mellan rubriken och innehållet.

   Raden rymmer alla tre även på en telefon: rubriken är två ord och
   knappen har sitt mått, så det är sökfältet som ger med sig. Wrap står
   kvar som utväg om en etikett någon gång blir längre än bredden. */
/* Raden är alltid lika hög, med eller utan kontroller. Sökfältet och
   knapparna mäter 2,75 rem och centrerar rubriken i den höjden; en rad
   utan dem — Statistik, en kort inkorg — blev annars lägre, och rubriken
   hoppade några punkter uppåt när man bytte flik. */
.rowSplit--tools { flex-wrap:wrap; gap:.75rem; min-height:2.75rem }
/* Rubriken tar sin bredd och inte mer. Den växte förut med raden, och
   luften den lade sig på var den enda luft sökfältet hade: på en telefon
   blev fältet sextio pixlar brett medan ordet "Sidor" hade hundratjugo. Ett
   ord behöver inte mer plats än ordet. */
/* Rubriken bär radhöjden själv, i stället för att ärva den av raden.

   .rowSplit--tools har min-height:2,75 rem just för att rubriken ska stå
   lika högt med eller utan kontroller bredvid sig. Det räcker bara så
   länge raden är EN rad. På telefon bryts den: sökfältet får flex-basis
   100 % och hamnar på rad två. Då slutar min-height gälla rubrikens rad —
   höjden fördelas på två rader som båda är så höga som sitt innehåll — och
   rubriken hamnar så högt som dess egen låda är.

   Följden var att Inkorg, som har ett fält men ingen skapa-knapp, stod
   4,7 px högre än Sidor, som har både och. Statistik och Assistent råkade
   ligga rätt, och rubriken hoppade alltså när man bytte flik.

   Radhöjden på rubriken själv löser det för varje kombination: lådan är
   2,75 rem vare sig något står bredvid eller inte, och halvledningen
   placerar tecknen på exakt samma linje som .bar__homeWord gör med sin
   centrering på Assistent. Blockelement, inte flex — text-overflow nedan
   kräver det. */
.rowSplit--tools .h1 { flex:0 1 auto; min-width:0; line-height:2.75rem;
  /* Rubriken får krympa men aldrig rita utanför sin ruta. Utan den här
     raden la sig "Media" ovanpå sökfältet i stället för att sluta där
     platsen tog slut.

     Paddingen är inte luft utan utrymme åt underlängderna. overflow:hidden
     klipper vid paddingkanten, och radhöjden 1,08 lämnar inte plats åt g
     och j — båda g:na i "Inlägg" kapades på mitten. Marginalen tar tillbaka
     det paddingen lade till, så raden står kvar där den stod. */
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  padding-bottom:.16em; margin-bottom:-.16em }

.wActions {
  display:flex; align-items:center; gap:.75rem;
  /* Basen avgör om raden håller ihop, och den ska väljas för den smalaste
     skärmen och inte för den bredaste.

     16rem var måttet förut, och med det fick "Inlägg · Nytt inlägg" inte
     plats bredvid rubriken på en telefon: raden bröt och sök hamnade på
     en egen rad under. 10,5 rem är knappens bredd plus ett fält att skriva
     i — allt över det växer den till ändå, för den får sin del av luften
     som blir kvar. */
  flex:1 1 10.5rem; min-width:0; justify-content:flex-end;
}
/* Lugnare hörn än appens piller. Raden står ovanför en tabell med
   rundade men inte runda hörn, och två helrunda kontroller över den drog
   blicken till kanterna i stället för till listan. */
/* Alla tre knapparna är lika breda: Ny sida, Nytt inlägg och Ny bild står
   på samma plats i tre vyer man växlar mellan, och en knapp som ändrar
   bredd när rubriken byts läses som att den hoppar. Måttet är den längsta
   etikettens naturliga bredd. */
.wActions .btn {
  /* 8rem: "Nytt inlägg" med sitt plustecken mäter 125,5 px och är den
     längsta av de tre. 6,5rem räckte innan ikonen kom till, och då hoppade
     knappen 21 px i bredd när man bytte flik. */
  width:auto; flex:0 0 auto; min-width:8rem;
  border-radius:.75rem;
}
/* Plustecknet står efter orden här. Framför läses det som en uppräkning
   av två saker; efter läses det som vad knappen gör med dem. Ordningen
   byts i stilen — märkspråket är samma knapp som överallt annars. */
.wActions .btn__ico { order:1; margin:0 -.125rem 0 0 }
/* Raden är listans egen kontrollrad. 44 px: lägre läste sig som något
   inklämt bredvid rubriken, högre tog över raden. Måttet gäller bara här —
   .btn--sm och .ctrl--search är kvar som de var överallt annars. */
.wActions .ctrl--search { border-radius:.75rem; min-height:2.75rem }
.wActions .btn { min-height:2.75rem }
/* 18rem och inte 24: fältet fyllde nästan halva raden och drog blicken
   från rubriken bredvid. Ett filter behöver rymma ett ord, inte en mening. */
.wActions .search { flex:1 1 auto; min-width:0; max-width:18rem }
/* Utan sökfält ska knappen ändå stå till höger. */
.wHead__fill { flex:1 1 auto }

/* Kuggen vänster om skapa-knappen: flikens egna inställningar.

   Konturen och inte fyllningen. Den fyllda knappen är radens ärende och
   ska vara ensam om att väga så; kuggen står innanför den som en tystare
   granne i samma höjd. Kvadratisk, för den bär bara tecknet — minsta
   bredden som håller skapa-knapparna lika breda gäller inte den. Ritas
   av kugge() i app.js. */
.wActions .wGear { min-width:0; width:2.75rem; padding:0; flex:0 0 auto }
.wActions .wGear .btn__ico { margin:0; width:1.125rem; height:1.125rem }

/* ---------- på en telefon: bara plustecknet ----------
   Rubriken, sökfältet och knappen delar en rad som är 390 punkter bred.
   "Nytt inlägg" tog nästan hundra av dem, och då fanns inget kvar: orden
   la sig över varandra och rubriken hamnade ovanpå fältet.

   Plustecknet säger samma sak. Det står redan i knappen på de breda
   skärmarna, efter orden, och är alltså inget nytt märke att lära sig —
   här är det bara orden som tas bort. Etiketten finns kvar för
   skärmläsaren via aria-label.

   Samma gräns som skalet: under 1100 px är det telefonens rad, över
   den skrivbordets. Gränsen gick vid 30 rem förut — "raden som inte får
   plats" — men då stod en surfplatta med skrivbordets knappar i
   telefonens skal, och två presentationer blandade i en vy läses som
   ett fel, inte som en anpassning. */
@media (max-width:1099px) {
  .wActions .btn__label,
  /* Projektlistan har samma rad och samma problem: en rubrik i 32 px och
     "Nytt projekt" bredvid den fyllde bredden och tvingade knappen ned på
     egen rad. Samma brytpunkt och samma utseende som Sidor och Inlägg —
     det är en och samma åtgärd på tre ställen. */
  .lobby__head .btn__label { display:none }
  /* Bara plustecknet, utan platta. En fylld knapp bredvid rubriken vägde
     tyngre än rubriken själv; ett mörkt plus säger samma sak med mindre
     röst. Träffytan är kvar som den var — det är bakgrunden som är borta,
     inte ytan — och pluset växer eftersom det inte längre har en platta
     som ger det tyngd. */
  .wActions .btn,
  .lobby__head .btn {
    min-width:0; width:2.75rem; flex:0 0 auto;
    padding:0; gap:0;
    background:none; color:var(--foreground);
    /* Rutan är 2,75 rem bred kring ett tecken på 1,75, och tecknet har
       dessutom luft inuti sin egen ruta. Utan kompensationen slutade
       plusets streck en bit innanför sökfältets kant rakt under, medan
       rubriken till vänster stod i linje: raden såg skev ut utan att
       något var fel på måtten. Marginalen flyttar bläcket ut till samma
       lodräta linje som fältet och listraderna. Ytan man trycker på
       följer med. */
    margin-right:-.75rem;
  }
  /* Stort och med tjockare streck: plustecknet står bredvid en fet rubrik
     i 32 px och ska väga som den, inte som en tunn glyf ur en list. */
  .wActions .btn__ico,
  .lobby__head .btn__ico { margin:0; width:1.75rem; height:1.75rem; stroke-width:2.5 }
  /* Kuggen mindre än pluset. Pluset är två streck och tål att växa;
     kuggen är en tät figur som i 28 px blir en klump bredvid rubriken. */
  .wActions .wGear .btn__ico { width:1.375rem; height:1.375rem; stroke-width:2 }
  /* Den negativa marginalen drar SISTA knappen ut till listkanten.
     Kuggen står före den och ska inte också dras — då hade de två lagt
     sig omlott. Konturen står kvar här, till skillnad från pluset som
     tappar sin platta: ett bart kugghjul bredvid ett bart plus läses
     som två tecken i en rad text, ringen gör det till en knapp. */
  .wActions .btn:not(:last-child) { margin-right:0 }
  /* Utan ring på telefonen, som pluset bredvid: två bara tecken i
     rubrikraden läses som ett par, en ring och ett plus som en knapp och
     ett tecken. Och tätt intill pluset — .5 rem in — så de står ihop
     som de kontroller de är, inte som två saker på var sin kant. */
  .wActions .wGear { width:2.5rem; min-height:2.5rem; box-shadow:none }
  .wActions .wGear:not(:last-child) { margin-right:-.5rem }

  /* ---------- sökfältet på egen rad ----------
     Tre saker om samma lista delar en rad som är 390 punkter bred:
     vilken lista man står i, vad man letar efter, och att lägga till
     något. Rubriken och plusset är korta och hör ihop — det ena säger
     var man är, det andra vad man kan göra här. Sökfältet är det enda
     man skriver i, och ett fält man skriver i ska vara brett.

     Behållaren löses upp med display:contents, så rubriken, knappen och
     fältet blir syskon i samma rad och kan brytas var för sig. Ordningen
     sätts i stilen och inte i märkspråket: fältet står kvar mellan
     rubriken och knappen i koden, där det hör hemma för den som läser
     med skärmläsare, och flyttas bara visuellt.

     Bara listorna som har en skapa-knapp. Inkorgen har rubrik och fält
     och ingenting mer — den raden har aldrig varit trång. */
  /* Gäller varje lista med den här raden — Sidor, Inlägg, Media och
     Inkorg. Fältet ska ligga på samma ställe oavsett vilken flik man
     står i; en kontroll som flyttar sig mellan skärmar är en kontroll
     man får leta efter varje gång. */
  .rowSplit--tools { flex-wrap:wrap; row-gap:.75rem }
  .rowSplit--tools .wActions { display:contents }
  .rowSplit--tools > .h1 { order:0; flex:1 1 auto }
  .rowSplit--tools .wHead__fill { display:none }
  .rowSplit--tools .btn { order:1 }
  .rowSplit--tools .search {
    order:2; flex:1 1 100%; max-width:none; margin:0;
  }

  /* Sökfältet är radens sista rad, och listan börjar direkt under.
     Luften mellan dem kom från hela radens undermarginal — borta här:
     listan hör till fältet man just filtrerat den med. Dubbelklassen
     slår grundregelns 1rem, som står senare i filen. */
  .screenHead > .rowSplit.rowSplit--tools { margin-bottom:0 }
}
.search--bare { margin:0 }
.search--bare .ctrl--search { padding-top:.5rem; padding-bottom:.5rem }

/* Flikraden som kortets översta rad.

   Sidor, Inlägg och Media stod förut som en egen ruta ovanför listan, med
   eget hörn och egen kant. Tre avgränsningar på fem centimeter — rubrik,
   flikrad, lista — och ögat läste tre saker där det finns en: det som
   ligger på webbplatsen, sett på tre sätt.

   Nu sitter de ihop: raden tappar sitt nedre hörn, kortet sitt övre, och
   kanten mellan dem ritas bara en gång. */
/* Stoppningen är radernas egen: tumnageln står 1 rem från kortets kant,
   och den valda flikens kant ska börja på samma linje. Raden blir högre av
   det, och det är rätt — den är kortets huvud och inte en kontroll som
   klämts in ovanför det. */
.seg--card { margin:0; padding:1rem;
  border-bottom-left-radius:0; border-bottom-right-radius:0 }
/* Grundregeln .seg--tabs + * ger nästa element 1,5 rem luft. Här ÄR nästa
   element resten av samma kort, och luft mitt i ett kort är ett glapp.

   Dubbelklassen behövs: .seg--card + * väger lika mycket som .seg--tabs + *
   och står tidigare i filen, alltså vann grundregeln. */
.seg.seg--card + * { margin-top:0 }
/* Men bara där kortet finns.

   Under 38 rem ritas inget kort — listkortet, statistikhöljet och
   bildrutnätets platta börjar alla först där — och flikraden har dessutom
   tappat sin egen platta (se .seg--card i mediefrågan längre ned). Då är
   nollan ovanför ett svar på en fråga som inte ställts: det som följer är
   inte kortets nedre halva, det är nästa sak på skärmen, och det låg dikt
   an mot flikarna. Tydligast på Media, där rutnätet börjar med en bildkant
   utan en enda pixel emellan.

   Samma gräns som korten, så att de inte kan glida isär, och samma mått
   som tomlägena redan bär nedanför. */
@media (max-width:1099px) {
  .seg.seg--card + * { margin-top:1.5rem }
}
/* Sociala medier stod inte med. Följden var att dess kort behöll sina
   rundade överhörn och sin överkant medan Sidor och Inkorg satt ihop med
   flikraden ovanför — samma skärm, två olika sätt att möta raden. */
#website-list > .ptableWrap,
#inbox-list > .ptableWrap,
#social-list > .ptableWrap {
  border-top-left-radius:0; border-top-right-radius:0; border-top:0;
}
/* Bildrutnätet hade ingen ram alls — som kortets nedre halva behöver det
   en, annars slutar raden ovanför i ingenting.

   Men bara där kortet finns. På en telefon tappar flikraden sin platta
   (se .seg--card i mediefrågan ovanför) och sidlistan får sitt kort först
   vid 38 rem — rutnätet var alltså det enda som bar en platta där, och en
   grå yta bakom tumnaglar som redan har egen form läses som en låda runt
   ingenting. Samma gräns som .ptableWrap: kortet gäller från 38 rem. */
@media (min-width:1100px) {
  #website-list > .mediaGrid {
    background:var(--card); border:1px solid var(--line); border-top:0;
    border-radius:0 0 1rem 1rem; padding:.75rem;
  }
}

/* Luften under flikraden ligger på det som följer, inte på raden själv.

   Marginalen satt förut inline på fem ställen i app.js och gick därför
   inte att ändra på ett ställe. Nu bär nästa element den, vilket ger
   samma avstånd på varje redigeringsskärm — och på Hemsida, där raden är
   sista barnet i det klistrade huvudet, matchar regeln ingenting alls. */
.seg--tabs + * { margin-top:1.5rem }

/* Flikraden på Hem står under rubriken och inte i den. De två hörde ihop
   så tätt att de lästes som ett block, och då slutade rubriken säga vad
   den stod över. Avståndet ligger på flikraden och inte på rubriken,
   eftersom rubriken bor i listen och flikraden i kroppen — två olika
   föräldrar, och en marginal som ska gälla mellan dem hör hemma på den
   som kommer sist. */
#dashboard-body > .seg--tabs { margin-top:.75rem }

/* Ett litet val bredvid en stor yta. Ingen kortyta och ingen fylld knapp:
   den valda plattan är ljus mot en dämpad bakgrund, vilket räcker för att
   se vilken den är utan att bli skärmens tyngsta element. */
.segWrap { display:flex; justify-content:center; margin-bottom:1rem }

/* Väljaren låg förut inuti ramen — först flytande över sidans nederkant,
   sedan i sajtens egen list. Båda placeringarna delade samma problem: den
   satt ovanpå något den inte hörde ihop med. Den bor i den flytande raden
   nu, se .llm--float .segWrap längre ned. */
.seg--mini { width:auto; gap:.125rem; padding:.1875rem;
  background:var(--surface-2); border-color:transparent; border-radius:.75rem }

/* ---- assistentvyn på telefon ----
   Reglerna står efter .preview och .llm, inte före: de väger lika tungt
   och det är ordningen som avgör. Skrivna tidigare i filen fick lådan sin
   höjd av samtalet på Hem och valrutan sträcktes från topp till botten. */

/* Sidan fyller det som blir över mellan flikarna och inmatningsraden. */
.preview--fill { flex:1 1 auto; min-height:0; border-radius:0 }
.preview--fill .preview__frame { width:100%; height:100% }

/* Skärmbreddsvalet står i sajtens egen list, till vänster om
   hamburgermenyn. Där finns redan luft, och valet lägger sig varken över
   logotypen eller mitt i innehållet. Listens höjd rapporteras inifrån
   ramen — reservvärdet gäller den bråkdel av en sekund innan första
   rapporten kommit. */
/* Väljaren bor i den flytande raden nu, inte i ramen. Den flyter med
   flödet ovanför skrivfältet — .llm ger avståndet — och behöver varken
   position eller marginal av egen kraft. Skuggan och den frostade
   bakgrunden följer med, för underlaget är fortfarande sajten. */
.llm--float .segWrap { margin:0; justify-content:center }
.llm--float .seg--mini {
  background:color-mix(in srgb, var(--surface-2) 88%, transparent);
  backdrop-filter:blur(10px); -webkit-backdrop-filter:blur(10px);
  box-shadow:0 .25rem 1rem rgba(10,10,10,.16);
}

/* Flikraden inne på en sida står tätare mot det den står över. Den är två
   ord breda knappar och behöver inte samma luft som en rubrik. */
@media (max-width:1099px) {
  #page-body .seg--ptab + * { margin-top:.875rem }
}

/* Raden svävar över sidan i stället för att ta plats under den. Luften
   ovanför och under var utrymme sajten hellre får ha, och en rad som
   ligger ovanpå läses ändå som en rad man skriver i.

   Två klassnamn för att väga tyngre än .llm, som står längre ned. */
#page-body:has(.llm--float) { position:relative; padding-bottom:0 }
.llm.llm--float {
  position:absolute; left:1.25rem; right:1.25rem; z-index:4;
  bottom:calc(.75rem + env(safe-area-inset-bottom));
  flex:0 0 auto; gap:.5rem; padding-top:0;
}
.llm--float .llm__bar { margin:0; box-shadow:0 .5rem 1.5rem rgba(10,10,10,.12) }

/* Rutan som dyker upp när assistenten arbetar.

   Den ligger över sidan och inte i ett flöde: på en telefon finns ingen
   plats för ett samtal bredvid det man tittar på, och svaret är oftast en
   mening. Ett svar i taget, och den försvinner när nästa fråga ställs. */
.llmPop {
  position:absolute; left:0; right:0; bottom:100%; z-index:3;
  display:flex; flex-direction:column; gap:.5rem;
  max-height:40dvh; overflow-y:auto;
  padding-bottom:.625rem;
  animation:llmPopIn .18s ease both;
}
/* Ligger över sidan och inte i flödet: rutan ska inte krympa vyn medan
   den syns. Det som är i bild är det assistenten utgår från, och att
   fråga något skulle inte ändra svaret på var man befinner sig. */
.llmPop .llm__m { max-width:100%; box-shadow:0 .5rem 1.5rem rgba(10,10,10,.12) }
.llmPop[hidden] { display:none }
@keyframes llmPopIn {
  from { opacity:0; transform:translate3d(0,.5rem,0) }
  to   { opacity:1; transform:none }
}
.seg--mini button { flex:0 0 auto; height:1.875rem; padding:0 .875rem;
  border-radius:.5rem; font-size:.75rem; font-weight:500;
  color:rgba(var(--ink-rgb),.62); box-shadow:none }
.seg--mini button.is-on { background:var(--card); color:var(--foreground);
  font-weight:600; box-shadow:0 1px 2px rgba(10,10,10,.1) }

/* ============================== taggar ============================
   Brickor som slås av och på. Samma kontroll för inläggets egna taggar och
   för vilka en inläggslista ska visa — det är samma fråga ställd åt två håll,
   och den ska se likadan ut. */
.tags { display:flex; flex-wrap:wrap; gap:.375rem }

/* Vald bär en platta, ovald bara sin kontur.
 *
 * Båda hade en yta förut, och skillnaden mellan dem var att den ena var
 * ljusare än den andra. På en rad med sex brickor där två är valda blev
 * det sex plattor att läsa igenom för att hitta de två — och det är den
 * enda frågan man ställer en tagglista.
 *
 * Utan fyllning är det bara de valda som har en yta, och de syns på
 * avstånd. Samma grepp som periodpillren under Statistik och som
 * .btn--ghost: kontur betyder valbar, fyllning betyder vald. */
.tag {
  padding:.4375rem .75rem; border-radius:var(--radius-pill);
  background:none; box-shadow:inset 0 0 0 1px var(--line);
  font-size:.8125rem; font-weight:500; color:rgba(var(--ink-rgb),.62);
  transition:background .15s, color .15s, box-shadow .15s;
}
@media (hover:hover) and (pointer:fine) {
  .tag:not(.is-on):hover {
    color:var(--foreground); box-shadow:inset 0 0 0 1px rgba(var(--ink-rgb),.3);
  }
}
.tag.is-on { background:var(--primary); color:var(--on-primary); box-shadow:none }

/* ============================== stilval ===========================
   Fler än tre alternativ ryms inte i en segmenterad rad på en telefon,
   och de här behöver dessutom en förklaring var. */
.pick { display:grid; grid-template-columns:1fr 1fr; gap:.5rem }
.pick button { padding:.875rem .875rem 1rem; text-align:left;
  background:var(--surface); border-radius:var(--radius-control);
  box-shadow:inset 0 0 0 1px var(--line); transition:box-shadow .15s, background .15s }
.pick button.is-on { background:var(--card); box-shadow:inset 0 0 0 2px var(--accent) }
.pick__label { display:block; font-size:.875rem; font-weight:600; letter-spacing:-.01em }
.pick__desc { display:block; margin-top:.1875rem; font-size:.6875rem; line-height:1.35;
  color:rgba(var(--ink-rgb),.58) }

/* ============================== tomma lägen =======================
   Streckad kant, samma som bildrutans. Ytan ensam räckte inte — den ligger
   tre steg från bakgrunden och syntes knappt, och ett tomt läge som inte
   syns är samma sak som inget tomt läge. Strecket säger dessutom vad rutan
   är: en plats som väntar på något, inte ett kort med innehåll i. */
.empty { padding:2.5rem 1.25rem; text-align:center; background:var(--surface);
  border:1px dashed var(--subtle); border-radius:var(--radius-card) }
.empty__title { font-size:.9375rem; font-weight:500 }
.empty__text { margin-top:.375rem; font-size:.8125rem; line-height:1.55; color:rgba(var(--ink-rgb),.58);
  max-width:34ch; margin-left:auto; margin-right:auto }

/* Tomt läge med ikon — används där en hel flik ännu saknar innehåll. */
/* Tomrutan är ett kort som alla andra: vit yta med en tunn kant. Den var
   grå, vilket gjorde den mörkare än listan den ersätter — en tom vy såg
   tyngre ut än en full. */
.placeholder { display:flex; flex-direction:column; align-items:center; text-align:center;
  padding:3.25rem 1.5rem; background:var(--card); border:1px solid var(--line);
  border-radius:var(--radius-card) }
.placeholder__ico { width:2.25rem; height:2.25rem; margin-bottom:1.125rem; color:var(--subtle);
  stroke:currentColor; fill:none; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:round }

/* ============================= fältgrupp ==========================
   Fält som hör ihop, i en egen ruta. Samma yta som ett kort i listan, så
   den läses som ett föremål bland andra föremål — och rubriken sitter
   inuti rutan och inte ovanför den, eftersom den namnger innehållet och
   inte avdelningen. */
.grp {
  margin-top:1.25rem; padding:1rem;
  background:var(--card); border:1px solid var(--line);
  border-radius:var(--radius-card);
}
.grp__t {
  margin:0 0 .75rem; font-size:.6875rem; font-weight:600;
  text-transform:uppercase; letter-spacing:.08em;
  color:rgba(var(--ink-rgb),.55);
}
/* Fälten inuti står tätare: rutan bär redan luften utåt. */
.grp > .field:first-of-type { margin-top:0 }

/* ============================ avancerat ===========================
   Det man söker upp, hopfällt och sist. Ett <details> och inte en egen
   växel: webbläsaren kan redan fälla ut, minns tillståndet medan skärmen
   står öppen och ger tangentbord och skärmläsare rätt beteende gratis.

   Linjen ovanför säger att det som kommer är en annan sorts inställning —
   inte nästa i raden, utan det som ligger bakom dem. */
/* Raden är hela tryckytan och står för sig själv: lika mycket luft över
   som under etiketten, så den hamnar mitt i sitt band. Förr bar fållan
   1,75 rem marginal OCH en linje ovanför, vilket la etiketten högt i
   bandet och lämnade ett stort tomrum under den — två fållor efter
   varandra blev två lösryckta streck i stället för en lista. */
/* Linjerna går ut till kanten, texten står kvar på spaltens linje.
 *
 * En avdelare som slutar där texten slutar läses som en understrykning av
 * raden ovanför. Går den hela vägen ut delar den i stället skärmen i två
 * band, och det är vad en fålla gör: allt ovanför är en sak, allt nedanför
 * en annan.
 *
 * Måttet är spaltens stoppning — .body bär 1,25 rem i både lådan på
 * skrivbordet och på telefonen. Det tas ut med negativ marginal och läggs
 * tillbaka som stoppning, så innehållet inte flyttar sig en enda pixel.
 * Står fållan någon gång i en spalt med annan stoppning sätts --bleed
 * där. */
.adv {
  --bleed:1.25rem;
  margin-top:1.5rem; margin-left:calc(-1 * var(--bleed)); margin-right:calc(-1 * var(--bleed));
  padding-left:var(--bleed); padding-right:var(--bleed);
  border-top:1px solid var(--line);
}
/* Fållor som följer på varandra staplas kant i kant. Linjen mellan dem är
   då EN linje och avståndet ett enda, som i vilken annan lista som helst
   i appen. Luften ovanför behövs bara före den första. */
.adv + .adv { margin-top:0 }
/* Och listan sluts nedåt, annars hänger sista raden i luften. */
.adv:last-of-type { border-bottom:1px solid var(--line) }

.adv__sum {
  display:flex; align-items:center; gap:.75rem; cursor:pointer;
  /* Hela bandet är tryckytan, kant till kant mellan linjerna.
   *
   * Den var en indragen pill förut: en rundad platta som lyste upp mitt i
   * raden medan linjerna gick förbi den på båda sidor. Det såg ut som att
   * något annat än raden markerades — och en fålla ÄR raden, hela vägen
   * ut. Marginalen tar därför ut .adv:s hela stoppning och lägger
   * tillbaka den som sin egen, plus ett kvarts steg: texten hamnar en
   * aning innanför linjens början, som i ett band. */
  margin:0 calc(-1 * var(--bleed)); padding:1rem calc(var(--bleed) + .25rem);
  border-radius:0; list-style:none;
  font-size:.9375rem; font-weight:600; letter-spacing:-.01em;
  color:rgba(var(--ink-rgb),.72);
  transition:background .15s ease, color .15s ease;
}
/* Safaris egen triangel bor i en pseudo som inte går att nå med den
   vanliga list-style-regeln. */
.adv__sum::-webkit-details-marker { display:none }
.adv__sum:hover { background:rgba(var(--ink-rgb),.05); color:var(--foreground) }
.adv__sum:active { background:rgba(var(--ink-rgb),.09) }
/* Utfälld fålla bär full färg. Det är den enda skillnaden som behövs för
   att se vilken av fem som står öppen utan att läsa innehållet. */
.adv[open] > .adv__sum { color:var(--foreground) }

/* Chevronen är en riktig chevron med runda ändar, ritad som en mask i
   textens egen färg — inte en roterad fyrkant av två kanter. Den gamla
   fyrkanten satt aldrig optiskt mitt i raden och flyttades med två
   magiska marginaler när den vändes; en mask vrids kring sin egen mitt
   och står stilla. */
.adv__sum::after {
  content:""; margin-left:auto; flex:0 0 auto;
  width:1.0625rem; height:1.0625rem;
  background:currentColor; opacity:.4;
  --chev:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9.5l6 6 6-6'/%3E%3C/svg%3E");
  -webkit-mask:var(--chev) center/1.0625rem 1.0625rem no-repeat;
  mask:var(--chev) center/1.0625rem 1.0625rem no-repeat;
  transition:transform .2s ease, opacity .15s ease;
}
.adv__sum:hover::after { opacity:.7 }
.adv[open] > .adv__sum::after { transform:rotate(180deg) }

/* Innehållet står på samma linje som fållans egen text (0,25 rem, se
   .adv__sum) och lämnar mer luft nedåt: nästa fålla börjar med en linje,
   och ett fält som ligger tätt över ett streck läses som att det hör till
   strecket i stället för till fållan det står i. */
/* Antalet i rubriken. Sex menylänkar syns då utan att fållan öppnas, och
   det är den enda frågan man har om en meny man inte tänkt röra. */
.adv__n {
  font-size:.75rem; font-weight:500; line-height:1;
  padding:.1875rem .4375rem; border-radius:var(--radius-pill);
  background:rgba(var(--ink-rgb),.08); color:rgba(var(--ink-rgb),.55);
}

/* Sidhuvudets och sidfotens fållor ligger i två föräldrar: menyn har sin
   egen sparväg och står utanför formuläret, fälten står i det. Utan de
   här raderna slutar menyn med en linje och fälten börjar med en egen —
   två streck där listan ska löpa obruten. */
#header-body > .adv:last-of-type,
#footer-body > .adv:last-of-type { border-bottom:0 }
#header-body > form > .adv:first-of-type,
#footer-body > form > .adv:first-of-type { margin-top:0 }

.adv__body { margin-top:.125rem; padding:0 .25rem 1.75rem }
/* Fältet inuti bär redan sin egen etikett; rutans rubrik säger vad
   gruppen är, och två rubriker på varandra är en för mycket. */
.adv__body > .field:first-child { margin-top:0 }

/* ============================== märken ============================ */
.badge { display:inline-flex; align-items:center; gap:.375rem; padding:.25rem .625rem;
  border-radius:var(--radius-pill); font-size:.6875rem; font-weight:600;
  text-transform:uppercase; letter-spacing:.05em }
.badge--owner { background:var(--badge-accent-bg); color:var(--badge-accent-fg) }
.badge--member { background:var(--surface-2); color:rgba(var(--ink-rgb),.62) }
.badge--pending { background:rgba(var(--ink-rgb),.06); color:rgba(var(--ink-rgb),.58) }
/* Ändrad sedan publiceringen. Accentfärgen och inte grönt: det som ligger
   ute är inte det man ser, och det är en skillnad man ska lägga märke
   till utan att den skriker fel. */
.badge--changed { background:var(--badge-accent-bg); color:var(--badge-accent-fg) }
/* Live är ett drifttillstånd, inte en roll. Egen färg därför att grönt
   betyder "fungerar" på ett sätt som accentfärgen aldrig kommer att göra. */
.badge--live { background:var(--badge-ok-bg); color:var(--badge-ok-fg) }
/* Läst i inkorgen. Egen klass och inte badge--pending: den senare säger
   "Väntar" på två andra ställen, och de ska inte byta utseende för att
   inkorgen ville ha ett fyllt märke. */
.badge--read { background:var(--badge-mute-bg); color:var(--badge-mute-fg) }

/* ---- flikraden på telefon ----
   Naken: ingen platta, ingen ram, ingen inre luft.

   Rutan runt flikarna gör nytta på en bred skärm, där den samlar tre
   knappar som annars svävar i en stor yta. På en telefon är raden redan
   hela bredden — då blir lådan en ruta runt en ruta, och den valda fliken
   en tredje platta inuti två andra. Kvar behövs bara markeringen av vilken
   flik man står på.

   Kortvarianten är samma rad som kortets huvud. Utan platta ska den inte
   heller bära kortets rundning eller kortets vaddering. */
@media (max-width:1099px) {
  .seg--tabs,
  .seg--card {
    padding:0; background:none; border:0; border-radius:0;
  }
  .seg--card { padding:0 0 .25rem }
  .seg--tabs button, .seg--card button { height:2.375rem }

  /* Den valda fliken bär --primary här också, men regeln står numera på
     .seg button.is-on och gäller alla bredder. Se den. */

  /* Ett tomt läge är inte kortets nedre halva.

     .seg.seg--card + * nollar luften under flikraden, för det som följer
     är normalt resten av samma kort. Är listan tom följer i stället en
     egen ruta — och den klistrade sig då mot flikarna, utan platta som
     band ihop dem. Den gäller bara här: på en bredare skärm finns kortet,
     och då ÄR rutan dess nedre halva. */
  #website-list > .empty,
  #website-list > .placeholder,
  .seg.seg--card + .empty,
  .seg.seg--card + .placeholder { margin-top:1.5rem }
}

/* ============================== flikrad ===========================
   På telefon: en rad längst ned, fem ikoner för tummen. På skrivbordet
   blir samma märkspråk en meny till vänster — se mediefrågan längst ned
   i filen. */
.tabbar {
  position:fixed; left:0; right:0; bottom:0; z-index:40;
  /* Raden är --tabbar hög, och under den ligger hemindikatorns zon. Den
     zonen är inte luft: bakgrunden ska gå ända ned, annars slutar menyn
     mitt på skärmen. Men ikonerna ska stå ovanför den, inte under
     strecket — därför både höjd och botteninlägg.

     Kommentaren här sa förut att zonen inte behövdes, för att det nativa
     skalet klippte underkanten själv. Det skalet finns inte längre. */
  height:var(--tabbarTotal);
  padding-bottom:var(--zonNed);
  display:none; justify-content:center; gap:.25rem;
  /* Heltäckande och inte frostad.

     Remsan mellan fönstrets kant och skärmens är död yta som bara rotens
     bakgrund når. Var flikraden genomskinlig fick den en annan ton än
     remsan under, och skarven mellan dem lyste som ett grått band tvärs
     över skärmen. Samma färg på båda, och remsan läses som menyns
     fortsättning i stället för som ett fel. */
  background:var(--background);
  border-top:1px solid var(--line);
  transform:translate3d(0,100%,0); transition:transform .25s cubic-bezier(.25,.46,.45,.94);
}
.tabbar.is-visible { display:flex; transform:translate3d(0,0,0) }
/* Gruppen finns för skrivbordets skull, där flikarna ska stå under
   varandra. På telefon är de fem flikar i samma rad, och då ska omslaget
   inte finnas i layouten alls. */
.tabbar__mid { display:contents }

/* Topplisten är skrivbordets. På telefon säger rubriken redan vilket
   projekt man är i, och klockan och kontot nås ur Hems egen rad. */
.topbar { display:none }

/* Märket nederst i menyn och projektväljaren överst hör till
   skrivbordet: på telefon är menyn en flikrad för tummen längst ned, och
   där är en logotyp varken en plats att gå till eller något att trycka
   på, och projektet står redan i Hems rubrik. */
.tabbar__end { display:none }
.tabbar__proj { display:none }

/* Hems list: rubriken till vänster som på varje annan flik, knapparna
   samlade till höger med notiserna ytterst. Egen flexrad — grundlistens
   rutnät är byggt för knapp-titel-knapp och har inga spalter för fyra.

   Måtten är rubrikradens på de andra flikarna — en rem ovanför, en rad på
   2,75 rem och .875 rem under — i stället för grundlistens fasta 3,5 rem.
   Rubriken ska stå på exakt samma punkt när man byter flik; listens egen
   höjd satte den drygt en halv rem högre än på Inkorg och Statistik. */
/* Luften mäts mellan tecknen och inte mellan rutorna.

   Kuggen och klockan satt i 2,75 rem breda rutor med .125 rem emellan:
   rutorna stod tätt, men tecknen i dem är 1,375 rem, så det blev 1,5 rem
   mellan bläcket i kuggen och bläcket i klockan mot .8125 rem upp till
   adressen bredvid. Tre saker på en rad med två olika avstånd läses som
   två grupper. Rutan är därför tecknets bredd här, och gapet är avståndet
   man faktiskt ser. Träffytan sitter kvar i höjden (2,75 rem) och i
   cirkeln som ::before ritar. */
.bar--home {
  display:flex; gap:.8125rem;
  height:auto;
  padding-top:calc(var(--top) + 1rem);
  padding-bottom:.875rem;
  /* center numera. Vänsterblocket var två rader högt när adressen stod
     som underrubrik; nu är det ett ord, och då hörde knappen och ikonerna
     inte längre ihop med en överkant utan med en mittlinje. */
  align-items:center;
}
/* Rubriken och domänen under den. Ordet får rubrikradens höjd (2,75 rem,
   samma som verktygsraderna på de andra flikarna) så Hem står på exakt
   samma punkt som Inkorg och Statistik; domänen är underrubriken, i samma
   grad som knappens text. */
/* Rubriken krymper. Den bar ordet "Assistent" — alltid lika brett, alltid
   med plats — och bär nu projektets namn, som kan vara hur långt som
   helst. Ett namn som inte får krympa trycker i stället ut klockan och
   kuggen ur raden. Avkortningen sker i .bar__homeName. */
.bar__homeTitle {
  flex:0 1 auto; min-width:0;
  display:flex; flex-direction:column; align-items:flex-start;
  /* Automarginalen skjuter ikonerna till högerkanten.

     Knappen ut till sajten stod förut mellan rubriken och ikonerna och
     höll isär dem. När den flyttade till brickraden ovanför skrivfältet
     föll mellanrummet bort, och klockan, kuggen och kontot hamnade
     hoptryckta intill ordet med hela skärmens bredd tom till höger. */
  margin-right:auto;
}
/* align-self:stretch, annars kortas ingenting av.

   .bar__homeTitle är en spalt med align-items:flex-start, och i en
   flexspalt får ett barn med flex-start sin egen innehållsbredd — det
   ritar alltså utanför föräldern i stället för att krympa med den, hur
   många min-width:0 som än står nedanför. Stretch binder rubriken till
   spaltens bredd, och först då har avkortningen något att mäta mot. */
.bar__homeWord { display:flex; align-items:center; min-height:2.75rem; margin:0;
  align-self:stretch; min-width:0 }
/* Namnet får krympa men aldrig rita utanför sin ruta.

   Paddingen är inte luft utan utrymme åt underlängderna: overflow:hidden
   klipper vid paddingkanten, och radhöjden 1,08 lämnar inte plats åt g och
   j. Marginalen tar tillbaka exakt det paddingen lade till — annars växer
   lådan, och en centrerad låda som växer flyttar texten uppåt. Rubriken
   ska ligga på samma linje som Hemsida, Inkorg och Statistik, som bär
   samma par i .rowSplit--tools .h1. */
.bar__homeName {
  min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  padding-bottom:.16em; margin-bottom:-.16em;
  /* Samma radhöjd som listvyernas rubriker (se .rowSplit--tools .h1), och
     inte centrering i en 2,75 rem hög låda. Två sätt att räkna fram samma
     mitt gav en tredjedels pixel isär — osynligt, men det är två sätt att
     underhålla. Ett räcker. */
  line-height:2.75rem;
  /* Men inte under 1100 px. Där bär rubriken växlaren i stället, se
     nedan — namnet står ändå i den adress som växlaren visar. */
  display:none;
}

/* Rubriken som projektväxlare, på skärmar utan topplist.

   Topplisten börjar vid 1100 px och tar med sig den enda vägen mellan
   projekt. Under den bredden stod projektets namn som en upplysning man
   inte kunde trycka på, och ett byte krävde kugghjulet och en lista två
   steg in — på just den skärm där det är krångligast att ta två steg.

   Samma piller som skrivbordets meny bär överst, i rubrikens storlek i
   stället för listens: den står ensam på en rad och ska läsas som en
   rubrik, inte som en knapp bland andra. Ingen ram i vila — pilen nedåt
   efter adressen säger att den går att byta, och ramen kommer först
   under fingret, som på skrivbordet under pekaren.

   max-width:100% och min-width:0 hela vägen ned: en kopplad domän kan
   vara hur lång som helst, och utan taket trycker pillret ut klockan och
   kuggen ur raden — samma skäl som .bar__homeName bar sin avkortning. */
.homeProj {
  display:inline-flex; align-items:center; gap:.4375rem;
  max-width:100%; min-width:0;
  margin:0; padding:.4375rem .75rem .4375rem .9375rem;
  border:0; border-radius:var(--radius-pill);
  background:transparent; color:var(--foreground);
  box-shadow:none;
  font-family:inherit; font-size:1.0625rem; font-weight:600;
  letter-spacing:-.015em; line-height:1.3;
  cursor:pointer;
}
.homeProj:active {
  background:rgba(var(--ink-rgb),.06);
  box-shadow:inset 0 0 0 1px rgba(var(--ink-rgb),.35);
}
.homeProj[hidden] { display:none }
.homeProj__addr {
  min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
/* Pilen säger vad ett tryck gör och inte vad raden handlar om, så den är
   dämpad mot adressen bredvid — samma förhållande som i menyn på
   skrivbordet. Vriden nedåt ur listornas högerpil. */
.homeProj__ico {
  flex:0 0 auto; width:1rem; height:1rem;
  transform:rotate(90deg);
  color:rgba(var(--ink-rgb),.55);
  stroke:currentColor; fill:none; stroke-width:2;
  stroke-linecap:round; stroke-linejoin:round;
}
/* Tryckytan är bredare än tecknet.

   Rutorna var 1,375 rem — 22 px — med 13 px emellan, alltså 35 px från
   mitt till mitt. Men cirkeln som ritas under fingret är 34 px, så två
   grannar låg en pixel isär: det SÅG ut som tre knappar och kändes som
   en remsa, och man träffade fel. 2,375 rem ruta med glappet neddraget
   ger 44 px mellan mitten och 10 px luft mellan cirklarna, utan att raden
   blir bredare än de 41 px den hade över. */
.bar--home .bar__btn { flex:0 0 auto; position:relative; width:2.375rem }
.bar--home .bar__btn + .bar__btn { margin-left:-.4375rem }
/* Cirkeln man trycker på. Ritas av ::before och inte av knappens egen
   ruta: rutan är smal för att avstånden ska stämma, och en tryckyta
   lika smal som ett tecken hade sett ut som ett streck. */
.bar--home .bar__btn::before {
  content:""; position:absolute; top:50%; left:50%;
  width:2.125rem; height:2.125rem; margin:-1.0625rem 0 0 -1.0625rem;
  border-radius:var(--radius-pill);
}
.bar--home .bar__btn:active { background:none }
.bar--home .bar__btn:active::before { background:var(--surface-2) }
/* Ingen utskjutning här. Den finns i grundlisten för att dra bläcket ut
   till sidmarginalen, och med rutan lika bred som tecknet ligger det
   redan där. */
.bar--home > .bar__btn:last-child { transform:none }
/* Siffran sitter i tecknets hörn, som förut — rutan runt den krympte. */
.bar--home .bar__count { top:.5rem; right:-.25rem }
.deskScrim, .deskBack { display:none }
/* Lobbyns bakåtpil är inte en skrivbordssak. Den finns så fort väljaren
   öppnats ovanpå ett projekt, och på telefonen behövs den mest: där
   täcker väljaren hela skärmen och det finns ingen meny att gå via. */
.deskBack.lobbyBack {
  display:inline-flex; align-items:center; justify-content:center;
  position:fixed; top:calc(var(--top) + .75rem); left:1.25rem; z-index:5;
  width:2.75rem; height:2.75rem;
  border-radius:var(--radius-pill);
  background:var(--card); color:var(--foreground);
  box-shadow:0 .25rem 1rem rgba(10,10,10,.14);
}
.deskBack.lobbyBack[hidden] { display:none }
.deskBack.lobbyBack svg {
  width:1.5rem; height:1.5rem; stroke:currentColor; fill:none;
  stroke-width:2; stroke-linecap:round; stroke-linejoin:round;
}
.deskBack.lobbyBack:active { transform:scale(.92) }

/* Och innehållet börjar under pilen, inte bakom den.

   Pilen är fastnålad i hörnet och ligger utanför flödet — den tar alltså
   ingen plats, och på en telefon börjar listan högst upp. Följden var att
   den låg mitt i ordet "Dina projekt": rubriken syntes som "…na projekt"
   med en cirkel över början.

   Bandet är pilens egen höjd plus luften den står i, så rubriken hamnar
   precis under den. Bara i över-läget: utan öppet projekt finns ingen pil
   och då ska lobbyn börja där den alltid börjat. */
#screen-projects.is-over .lobby { padding-top:calc(var(--top) + 4.75rem) }
.tab {
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:.125rem;
  flex:1 1 0; max-width:5rem; min-width:0; color:rgba(var(--ink-rgb),.55);
}
/* Större tecken när ordet är borta.
   1,25 rem var måttet på en ikon med en etikett under sig — tillsammans
   var de flikens tyngd. Ensam är samma ikon en prick i en rad på 68 px,
   och det man siktar på ska synas. */
.tab svg { width:1.5rem; height:1.5rem; stroke:currentColor; fill:none;
  stroke-width:1.75; stroke-linecap:round; stroke-linejoin:round }
.tab span { font-size:.625rem; font-weight:500; letter-spacing:.01em }

/* Etiketten göms för ögat men inte för skärmläsaren.
 *
 * Fem ord på fem flikar är fem ord ingen läser: raden lärs in på
 * ikonernas plats efter andra dagen, och orden under dem stal höjd från
 * det man faktiskt siktar på. Skrivbordets meny behåller dem — där står
 * de bredvid ikonen och raden har plats.
 *
 * clip-path och inte display:none. Knappen har ingen annan text, så ett
 * dolt ord hade lämnat fem namnlösa knappar för den som lyssnar sig
 * fram. */
.tab__l {
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip-path:inset(50%); white-space:nowrap;
}
/* Den aktiva flikens ikon bär accentfärgen — samma orange som
   frontness.se. Ordet står kvar i bläck: ett ord i orange hade sett ut
   som en länk, en ikon i orange ser ut som ett val. Gäller telefonens
   flikrad och skrivbordets meny lika. */
.tab.is-active { color:var(--foreground) }
.tab.is-active svg { stroke-width:2; stroke:var(--accent) }
/* Liten prick när det finns obesvarade inbjudningar. */

/* ============================ menyredigeraren =====================
   En rad per länk: grepp, namn, mål och — i sidhuvudet — om den ska stå
   som text eller som knapp. Raderna är kort och inte listrader: de bär
   fält man skriver i, och ett fält i en rad man kan trycka på blir en rad
   man trycker på av misstag. */
.menuList { display:flex; flex-direction:column; gap:.5rem }
.menuRow {
  display:grid; grid-template-columns:1.5rem minmax(0, 1fr) 2rem;
  align-items:start; gap:.5rem;
  padding:.625rem .5rem; border-radius:var(--radius-control);
  background:var(--card); border:1px solid var(--line);
}
.menuRow__grip {
  display:grid; place-items:center; height:2.25rem;
  color:rgba(var(--ink-rgb),.3); cursor:grab;
}
.menuRow__grip:active { cursor:grabbing }
.menuRow__grip svg {
  width:1rem; height:1rem; stroke:currentColor; fill:none;
  stroke-width:1.75; stroke-linecap:round; stroke-linejoin:round;
}
.menuRow__body { display:flex; flex-direction:column; gap:.375rem; min-width:0 }
.menuRow__style { padding-top:.125rem }
.menuRow__x {
  display:grid; place-items:center; width:2rem; height:2.25rem;
  border-radius:.5rem; color:rgba(var(--ink-rgb),.35);
}
.menuRow__x:hover { background:var(--surface-2); color:var(--danger) }
.menuRow__x svg {
  width:.9375rem; height:.9375rem; stroke:currentColor; fill:none;
  stroke-width:2; stroke-linecap:round;
}
.menuRow--ghost { opacity:.35 }

/* Fälten i en rad är mindre än de i ett formulär: raden är en rad och
   inte ett stycke, och tre fullstora fält i den hade blivit en skärm. */
.ctrl--sm { padding:.4375rem .625rem; font-size:.875rem; border-radius:.5rem }
.ctrl--sm.ctrl--select { padding-right:2rem; background-size:1rem }

/* På en bred skärm står namnet och målet bredvid varandra. */
@media (min-width:38rem) {
  .menuRow__body { flex-direction:row; align-items:center; flex-wrap:wrap }
  .menuRow__body > .ctrl { flex:1 1 8rem; min-width:0 }
  .menuRow__style { flex:0 0 auto; padding-top:0 }
}

/* ============================ notispanelen ========================
   Fälls ned under klockan. Fast höjd med egen rullning: listan ska aldrig
   äta fönstret, och kanten som skär av sista raden säger att det finns
   mer utan att någon behöver skriva det.

   På telefon står klockan i Hems rad uppe till vänster, och panelen tar
   bredden mellan kanterna. På skrivbordet hänger den under klockan i
   listens högerkant. */
.noticePop {
  position:fixed; z-index:70;
  top:calc(var(--top) + 3.25rem); left:.75rem; right:.75rem;
  display:flex; flex-direction:column;
  max-height:min(26rem, calc(100dvh - var(--top) - 6rem));
  background:var(--card); border:1px solid var(--line);
  border-radius:1rem; box-shadow:0 1rem 2.5rem rgba(10,10,10,.16);
  overflow:hidden;
  opacity:0; transform:translateY(-.5rem);
  transition:opacity .18s ease, transform .18s cubic-bezier(.25,.46,.45,.94);
}
.noticePop.is-open { opacity:1; transform:none }
.noticePop[hidden] { display:none }

.noticePop__head {
  flex:0 0 auto;
  display:flex; align-items:center; justify-content:space-between;
  padding:.875rem 1rem; border-bottom:1px solid var(--line);
}
.noticePop__title { font-size:.9375rem; font-weight:600; letter-spacing:-.01em }
.noticePop__x {
  border:0; background:none; color:rgba(var(--ink-rgb),.55); cursor:pointer;
  font-size:1.25rem; line-height:1; padding:.125rem .375rem;
}
.noticePop__list { flex:1 1 auto; min-height:0; overflow-y:auto; padding:.375rem }
/* Tomrutan har ingen egen ram här. .placeholder är ett kort, och ett
   kort inuti panelen ger två ramar med två millimeter emellan — det
   läses som en ruta i en ruta i stället för som ett lugnt tomt läge.
   Panelen är redan inramningen. */
.noticePop .placeholder { background:none; border:0; padding:2.75rem 1.25rem }

/* Ytan bakom. Osynlig men klickbar: ett tryck utanför stänger, som varje
   nedfälld ruta någon använt. */
.noticePop__scrim { position:fixed; inset:0; z-index:69 }
.noticePop__scrim[hidden] { display:none }

.notice {
  display:flex; align-items:flex-start; gap:.75rem; width:100%;
  padding:.75rem; border-radius:var(--radius-control);
  text-align:left; color:inherit;
}
.notice:hover { background:var(--surface-2) }
.notice__ico {
  flex:0 0 auto; display:grid; place-items:center;
  width:2rem; height:2rem; border-radius:var(--radius-pill);
  background:var(--surface-2); color:rgba(var(--ink-rgb),.62);
}
.notice__ico svg {
  width:1rem; height:1rem; stroke:currentColor; fill:none;
  stroke-width:1.75; stroke-linecap:round; stroke-linejoin:round;
}
.notice__text { display:flex; flex-direction:column; gap:.125rem; min-width:0 }
.notice__title { font-size:.875rem; font-weight:500; line-height:1.35 }
.notice__sub {
  font-size:.8125rem; color:rgba(var(--ink-rgb),.62); line-height:1.4;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.notice__when { font-size:.6875rem; color:rgba(var(--ink-rgb),.5); margin-top:.125rem }

/* Det som ännu inte tagits om hand står kvar i fetare stil. Sett är inte
   samma sak som klart, och panelen ska visa skillnaden. */
.notice--unread .notice__title { font-weight:600 }
.notice--unread .notice__ico { background:rgba(177,95,44,.12); color:var(--accent-to) }

/* ============================== kartan ============================
   Minnet som en graf. Canvasen fyller vyn och bär allt: linjerna, noderna
   och namnen. Ingen ram och ingen bakgrundsfärg — det som ritas ska ligga
   direkt på appens yta. */
/* Raden om hur många en sökning gav. Ligger mellan flikraden och kortet
   och skulle bryta isär dem — därför inuti kortets övre kant i stället:
   den hör till listan under, inte till flikarna över. */
.lead--count {
  margin:0; padding:.875rem 1rem;
  background:var(--card); border:1px solid var(--line); border-top:0;
  font-size:.8125rem;
}
.lead--count + .ptableWrap { border-top-left-radius:0; border-top-right-radius:0 }

/* Statistikens kort: ett hölje med de andra inuti.

   Vyn var en trave fristående rutor på sidans egen yta — flikrad,
   periodrad, fem kort — och sju kanter under varandra läses som sju saker
   fast det är en. Nu ligger allt i ett hölje som fortsätter på flikraden
   ovanför, precis som listorna på Hemsida.

   Korten inuti behåller sina egna kanter: det är de som skiljer besökarna
   från kurvan från måtten, och utan dem blir kortet en enda gröt. Det som
   försvinner är den yttersta ramen runt varje enskilt. */
.statWrap {
  /* .75 rem mellan korten och 1 rem ut till kanten. Ett helt rem emellan
     läste sig som lika mycket avstånd som ut till ramen, och då slutade
     korten höra ihop — de ligger i samma hölje och ska stå tätare med
     varandra än med omvärlden. */
  display:flex; flex-direction:column; gap:.75rem;
}
/* Korten bär margin-top från .card + .card, som väger lika tungt som
   .statWrap > * och står senare i filen. Dubblerad klass för att gå före:
   avståndet ska komma från radens gap och ingen annanstans. */
.statWrap.statWrap > .card,
.statWrap.statWrap > .placeholder { margin:0 }
/* Marginalerna satt på korten och blir dubbla i en flex med gap. */
.statWrap > * { margin:0 }

/* Periodraden linjerar med korten under sig.
 *
 * Raden är byggd för att kunna rulla kant i kant: negativ marginal ut till
 * skärmkanten och samma mått som stoppning tillbaka. Regeln ovanför nollar
 * marginalen men inte stoppningen, och då stod pillren kvar 1,25 rem
 * längre in än allt annat på skärmen — som en rad som glidit åt höger.
 *
 * Stoppningen bort alltså, och luften under raden hit i stället: radens
 * eget gap (0,75 rem) räckte inte när filtret ändrar allt under sig, och
 * en kontroll som styr en hel vy ska ha en tydlig lucka efter sig. */
.statWrap .periods { margin:0 0 .5rem; padding:0 0 .25rem }


/* Höljet finns bara där flikraden är ett kort.

   På en telefon tappar .seg--card sin platta (se mediefrågan vid
   flikraden), och då blev höljet en grå låda som började mitt i tomma
   luften — en ram runt sju kort som redan har egna ramar. Samma gräns som
   .ptableWrap och bildrutnätet: kortet gäller från 38 rem, och därunder
   står korten direkt på sidans yta.

   Färgerna inuti hör till höljet och följer med. De ligger ett steg upp
   FRÅN höljet — utan hölje finns inget att ligga ett steg upp från, och
   då ska korten ha sin vanliga kortfärg som alla andra kort i appen. */
@media (min-width:1100px) {
  .statWrap {
    /* Samma yta som listkortet under Sidor och Inkorg: --card. Höljet är
       samma sorts sak i alla tre vyer och ska se likadant ut.

       Skillnaden görs på det som ligger INUTI. Korten bar också --card,
       och då blev allt en enda färg där bara hårfina kanter skilde
       delarna åt — en ram runt något som har samma färg som allt annat
       läses inte som en ram. Ett steg ljusare räcker. */
    background:var(--card); border:1px solid var(--line); border-top:0;
    border-radius:0 0 1rem 1rem; padding:1rem;
  }
  /* Ett steg upp från höljet. Kanten får gå: när ytorna skiljer sig behövs
     ingen linje för att säga var den ena slutar, och två markeringar för
     en gräns är en för mycket. */
  .statWrap .statTile { background:var(--surface-2) }
  .statWrap .statTile:hover { background:var(--surface) }
  /* Alla kort i höljet bär samma ton.
   *
   * Brickorna med besök och avvisningsfrekvens låg på --surface-2 medan
   * listorna under dem låg på --card. Skillnaden är ett steg och syntes
   * knappt var för sig, men på samma skärm läste den som två sorters
   * kort — och de gör samma sak: de visar siffror i samma hölje. Ett
   * hölje, en ton.
   *
   * Uppräkningen är avsiktligt lång i stället för ".statWrap .card".
   * Två av korten i höljet är inte kort utan rutnät — .statTiles och
   * .kpis ritar sina skiljelinjer med sin egen bakgrund i gapen — och
   * en gemensam regel hade slätat ut linjerna mellan brickorna. */
  .statWrap .chartCard,
  .statWrap .statList,
  .statWrap .revRow,
  .statWrap .scStatus,
  .statWrap .goal,
  /* Kortet med de två stora talen och källstapeln: samma ton som
     brickorna det bär, annars stod stapelns del av kortet ett steg
     mörkare än brickorna ovanför och såg ut att sakna botten. */
  .statWrap .statCard--box,
  .statWrap .placeholder {
    background:var(--surface-2); border-color:transparent;
  }

  /* Nyckeltalen på Synlighet och Omdömen: rutorna är kort och linjerna
     mellan dem är höljets färg som syns i gapen. Samma två toner som
     brickorna under Besök, bara åt andra hållet — där är rutnätet --card
     och brickorna ljusare. */
  .statWrap .kpi { background:var(--surface-2) }
  .statWrap .kpis { background:var(--card); border-color:transparent }
  /* Rutnätens 1px-linjer ritas av behållarens bakgrund. Mot en ljusare
     bricka syns --line knappt; höljets egen färg gör linjen till ett spår
     i stället för ett streck. */
  .statWrap .statTiles,
  .statWrap .metricStrip { background:var(--card); border-color:transparent }
  /* Periodraden sköts av regeln vid .statWrap och gäller alla bredder:
     samma indrag som korten, samma luft under. Inuti kortet är kanten
     kortets, och det är samma svar. */
}

/* Vägarna bort från ett svar. Text och inte knappar: att svara gör man med
   knapparna ovanför, det här handlar om vad som ska hända med raden efteråt.
   Centrerade och med luft över, som en fot. */
.subActions {
  display:flex; flex-direction:column; align-items:center; gap:.25rem;
  margin-top:2.5rem;
}
.subActions .btn { width:auto; min-height:2.5rem; padding:.5rem 1rem }
.btn--plainDanger { color:var(--danger) }
.btn--plainDanger:active { color:var(--danger) }

/* Rubrik per sort i anteckningslistan.

   Lägre än .sectionTitle och med versaler kvar: den delar upp ett stycke,
   den öppnar inte ett nytt. Pricken bär nodens färg från globen — det är
   den som gör rubriken till en färgförklaring på köpet.

   Första rubriken behöver ingen luft över sig; den står redan under
   Anteckningar. Resten gör det, annars flyter grupperna ihop. */
.memGrupp {
  display:flex; align-items:center; gap:.5rem;
  margin:1.5rem 0 .5rem; font-size:.6875rem; font-weight:600;
  letter-spacing:.08em; text-transform:uppercase;
  color:rgba(var(--ink-rgb),.55);
}
.memGrupp:first-of-type { margin-top:1rem }
.memGrupp__prick { flex:0 0 auto; width:.5rem; height:.5rem; border-radius:50% }
/* Antalet väger mindre än namnet: det är en upplysning, inte en etikett. */
.memGrupp__n { margin-left:auto; font-weight:500; letter-spacing:0;
  color:rgba(var(--ink-rgb),.38); font-variant-numeric:tabular-nums }

/* Vad minnet består av, under globen.

   En rad brickor och inte en tabell: talen är små och få, och en tabell
   för sju rader med ett tal i varje är en ram runt ingenting. Pricken är
   samma färg som noden har på globen — det är den som gör raden till en
   färgförklaring och inte bara en uppräkning. */
.memStats { display:flex; flex-wrap:wrap; gap:.5rem .625rem; margin-top:1rem }
.memStat {
  display:inline-flex; align-items:center; gap:.375rem;
  padding:.3125rem .625rem; border-radius:var(--radius-pill);
  background:var(--card); border:1px solid var(--line);
}
.memStat__prick { flex:0 0 auto; width:.5rem; height:.5rem; border-radius:50% }
.memStat__n { font-size:.8125rem; font-weight:600; font-variant-numeric:tabular-nums }
.memStat__l { font-size:.8125rem; color:rgba(var(--ink-rgb),.62) }
.memStats__fot { margin:.75rem 0 0; font-size:.75rem; line-height:1.55;
  color:rgba(var(--ink-rgb),.5) }

/* Globen som ingång till minnet.

   En tredjedel av höjden: nog för att den ska snurra och säga vad vyn är,
   för lite för att stå i vägen för listan under. dvh och inte vh — på en
   telefon med adressfält är vh fönstret som det ser ut när fältet är
   undanrullat, alltså större än det man faktiskt har. */
.memHero { flex:0 0 auto; height:33dvh; min-height:11rem; max-height:22rem;
  display:flex; flex-direction:column }

.graf { position:relative; flex:1 1 auto; min-height:0; margin:0 -1.25rem }
.graf__c { display:block; width:100%; height:100%; touch-action:none; cursor:grab }
.graf__c:active { cursor:grabbing }

/* ============================== auth-skärmar ======================
   Bläcksvart yta med vit logotyp, samma grepp som sidfoten på frontness.se.
   Varpen — lodräta hårstreck — är webbplatsens vävmotiv. */
.ink { background:var(--ink); color:#fff }
.ink .bar__btn { color:#fff }
.ink .bar__btn:active { background:rgba(255,255,255,.08) }
.ink .lead { color:rgba(255,255,255,.6) }
.ink .btn--plain { color:rgba(255,255,255,.6) }
.ink .muted { color:rgba(255,255,255,.55) }

.warp {
  position:absolute; inset:0; z-index:0; pointer-events:none; opacity:.5;
  background:repeating-linear-gradient(90deg, rgba(255,255,255,.07) 0 1px, transparent 1px 14px);
}
/* Terrakottaglöden i nederkanten: samma lutning som knapparna på webbplatsen,
   utsuddad till ett ljus i stället för en yta.

   Ellipsen ritas inuti elementets egna kanter i stället för att låta en stor
   cirkel svämma över dem. Ett absolut placerat barn som sticker utanför sin
   rullbehållare räknas nämligen in i scrollWidth och scrollHeight, och gav
   både vågrät och lodrät rullning på en skärm som annars rymdes precis. */
.glow {
  position:absolute; left:0; right:0; bottom:0; height:55%;
  z-index:0; pointer-events:none;
  background:radial-gradient(ellipse 85% 100% at 50% 118%,
    rgba(207,128,71,.38) 0%, rgba(207,128,71,.12) 45%, rgba(207,128,71,0) 70%);
}

.hero { position:relative; z-index:1; display:flex; flex-direction:column; flex:1 1 auto;
  padding:0 1.5rem calc(2rem + env(safe-area-inset-bottom)) }
.hero__top { padding-top:calc(3.5rem + var(--top)) }
.hero__logo { width:9.5rem; height:auto }
/* Två logotyper ligger i märkspråket, en per bakgrund. Telefonen är mörk,
   så den ljusa gäller; skrivbordets gästlayout vänder på det. Utan den här
   raden ritas båda, ovanpå varandra. */
.hero__logo--onLight { display:none }
.hero__mid { margin-top:auto; padding:3rem 0 2.5rem }
.hero__mid h1 { font-size:2.5rem; font-weight:600; letter-spacing:-.035em; line-height:1.05 }
.hero__mid p { margin-top:1rem; max-width:26ch; font-size:.9375rem; line-height:1.65; color:rgba(255,255,255,.6) }

.authForm { position:relative; z-index:1; display:flex; flex-direction:column; flex:1 1 auto;
  padding:1.5rem 1.5rem calc(2rem + env(safe-area-inset-bottom)) }
.authForm__head { margin-bottom:1.75rem }
.authForm__head h1 { font-size:1.75rem; font-weight:600; letter-spacing:-.03em; line-height:1.1 }
.authForm__head p { margin-top:.625rem; font-size:.9375rem; line-height:1.6; color:rgba(255,255,255,.6) }
.authForm__foot { margin-top:auto; padding-top:1.5rem }

/* ============================== notiser =========================== */
#toasts {
  position:fixed; left:50%; bottom:calc(1rem + env(safe-area-inset-bottom)); z-index:9999;
  transform:translateX(-50%);
  display:flex; flex-direction:column; align-items:center; gap:.5rem;
  width:calc(100% - 2.5rem); max-width:26rem; pointer-events:none;
}
.toast {
  width:100%; padding:.875rem 1.125rem; border-radius:var(--radius-control);
  background:var(--primary); color:var(--on-primary); font-size:.875rem; line-height:1.4; text-align:center;
  opacity:0; transform:translate3d(0,.75rem,0);
  transition:opacity .25s cubic-bezier(.25,.46,.45,.94), transform .25s cubic-bezier(.25,.46,.45,.94);
}
.toast.is-in { opacity:1; transform:translate3d(0,0,0) }
/* Notisen som ligger kvar medan något pågår. Snurran till vänster och
   texten tätt intill: raden är ett besked om att arbetet fortsätter, inte
   en rubrik. Centreringen släpps därför — två rader text som centreras
   kring en snurra läses som två olika saker. */
.toast--hold { display:flex; align-items:center; gap:.625rem; text-align:left }
.toast--hold .spin { flex:0 0 auto }
.toast--ok { background:var(--ok) }
.toast--err { background:var(--danger) }
.toast--warn { background:var(--accent-to) }
/* När flikraden syns måste notiserna lyfta över den. */
body.has-tabs #toasts { bottom:calc(var(--tabbarTotal) + .75rem) }

/* ============================== dialog ============================
   Ersätter prompt()/confirm(). De blockerar webbvyn i den nativa appen
   och ser dessutom ut som en webbsida, inte som appen. */
.sheet { position:fixed; inset:0; z-index:200; display:none }
/* Felet från arkets granskare. Rött och litet, mellan innehållet och
   knapparna — där blicken går på väg mot den knapp som just inte gjorde
   något. */
.sheet__err {
  margin:.875rem 0 0; font-size:.8125rem; line-height:1.5;
  color:var(--danger);
}
.sheet__err[hidden] { display:none }

/* =============== feedback ==================
   Skärmdumpar i en rad medan man skriver, och listan över det man skickat
   under formuläret. Bilderna är små med flit: de är bevis och inte
   innehåll — man ska se ATT de är med, och kunna öppna dem i full storlek
   om man vill titta. */
/* ---------- förbrukningen ----------
   Summan först och stor, delarna under den som staplar. Talen är små och
   abstrakta — "1 240 000 tecken" säger ingenting någon känner igen — så
   det är kronorna som får typografin och tecknen som står i det finstilta. */
.useSum { display:flex; flex-direction:column; gap:.125rem; margin-bottom:1.25rem }
.useSum__kr { font-size:1.75rem; font-weight:600; letter-spacing:-.02em;
  font-variant-numeric:tabular-nums }
.useSum__tal { font-size:.8125rem; color:rgba(var(--ink-rgb),.55);
  font-variant-numeric:tabular-nums }

.useList { display:flex; flex-direction:column; gap:1rem }
.useRow { display:flex; flex-direction:column; gap:.375rem }
.useRow__top { display:flex; align-items:baseline; justify-content:space-between; gap:1rem }
.useRow__namn { font-size:.9375rem; font-weight:500; letter-spacing:-.01em }
/* tabular-nums så beloppen står på linje i högerkanten. Utan den vandrar
   kommatecknet i sidled rad för rad och kolumnen slutar vara en kolumn. */
.useRow__kr { flex:0 0 auto; font-size:.875rem;
  color:rgba(var(--ink-rgb),.62); font-variant-numeric:tabular-nums }
.useRow__sub { font-size:.75rem; line-height:1.5; color:rgba(var(--ink-rgb),.5) }

/* Stapeln säger andel, inte mängd. Den ska gå att jämföra med raden över
   och under — därför skalas alla mot den dyraste posten och inte mot
   summan: med summan som nämnare blir sex jämnstora poster sex korta
   stumpar som inte säger något alls. */
.useRow__bar { height:.375rem; border-radius:999px;
  background:rgba(var(--ink-rgb),.08); overflow:hidden }
.useRow__bar i { display:block; height:100%; border-radius:999px;
  background:var(--accent) }

.fbShots { display:flex; flex-wrap:wrap; gap:.5rem }
.fbShot { position:relative; width:5rem; height:5rem; flex:0 0 auto }
.fbShot img {
  width:100%; height:100%; object-fit:cover;
  border-radius:var(--radius-control); border:1px solid var(--line);
}
.fbShot__x {
  position:absolute; top:-.375rem; right:-.375rem;
  display:grid; place-items:center; width:1.5rem; height:1.5rem;
  border:0; border-radius:50%; padding:0; cursor:pointer;
  background:rgba(10,10,10,.75); color:#fff;
}
.fbShot__x svg {
  width:.75rem; height:.75rem; stroke:currentColor; fill:none;
  stroke-width:2.5; stroke-linecap:round;
}

.fbList { display:flex; flex-direction:column; gap:.75rem }
.fbItem {
  display:flex; flex-direction:column; gap:.4375rem;
  padding:1rem 1.125rem;
  background:var(--card); border:1px solid var(--line);
  border-radius:var(--radius-card);
}
/* Tiden först och i mindre grad: den svarar på "när sa jag det här?",
   vilket är frågan man ställer när man läser sin egen lista. */
.fbItem__när {
  font-size:.6875rem; font-weight:600; letter-spacing:.06em;
  text-transform:uppercase; color:rgba(var(--ink-rgb),.45);
  font-variant-numeric:tabular-nums;
}
.fbItem__text { margin:0; font-size:.9375rem; line-height:1.55; white-space:pre-wrap }
.fbItem__bilder { display:flex; flex-wrap:wrap; gap:.375rem; margin-top:.25rem }
.fbItem__bilder img {
  width:3.5rem; height:3.5rem; object-fit:cover;
  border-radius:.5rem; border:1px solid var(--line);
}

/* =============== arbetsrutan ==================
   Medan verktyget skriver eller ritar. Täcker skärmen med flit: det som
   pågår ska bli klart innan man går vidare, och en ruta man kan klicka
   förbi är en ruta som lovar att man kan det. */
.work {
  position:fixed; inset:0; z-index:210;
  display:grid; place-items:center;
  background:color-mix(in srgb, var(--background) 78%, transparent);
  backdrop-filter:blur(6px); -webkit-backdrop-filter:blur(6px);
}
.work__box {
  display:flex; flex-direction:column; align-items:center; gap:.5rem;
  max-width:20rem; padding:2rem 1.75rem; text-align:center;
  background:var(--card); border:1px solid var(--line);
  border-radius:var(--radius-card);
  box-shadow:0 1rem 3rem rgba(10,10,10,.28);
}
.work__spin {
  width:1.75rem; height:1.75rem; margin-bottom:.375rem;
  border:2px solid rgba(var(--ink-rgb),.18);
  border-top-color:var(--accent); border-radius:50%;
  animation:workSpin .8s linear infinite;
}
@keyframes workSpin { to { transform:rotate(360deg) } }
@media (prefers-reduced-motion:reduce) {
  .work__spin { animation-duration:2.4s }
}
.work__title { font-size:1rem; font-weight:600; letter-spacing:-.01em }
.work__text  { font-size:.875rem; line-height:1.5; color:rgba(var(--ink-rgb),.62) }
/* Tiden i siffror som inte hoppar i bredd när sekunderna byts. */
.work__time  {
  margin-top:.375rem; font-size:.8125rem; color:rgba(var(--ink-rgb),.45);
  font-variant-numeric:tabular-nums;
}
.sheet.is-open { display:block }
.sheet__scrim { position:absolute; inset:0; background:rgba(10,10,10,.45);
  opacity:0; transition:opacity .25s }
.sheet.is-in .sheet__scrim { opacity:1 }
/* Kanten runt lådan.

   Ytan är samma svarta som appen bakom, och i mörkt läge räckte inte
   skuggan för att säga var lådan slutar — den flöt ihop med det den låg
   över. En innerskugga och inte en border: lådan rullar invändigt, och en
   border hade följt med innehållet i stället för att stå kvar runt rutan.

   Ljust läge har redan en synlig kant genom sin skugga och behöver bara
   ett hårstreck; mörkt läge får en aning mer i sitt eget block. */
.sheet__panel {
  position:absolute; left:0; right:0; bottom:0;
  background:var(--background); border-radius:1.5rem 1.5rem 0 0;
  padding:1.5rem 1.25rem calc(1.5rem + env(safe-area-inset-bottom));
  transform:translate3d(0,100%,0); transition:transform .3s cubic-bezier(.25,.46,.45,.94);
  max-height:88dvh; overflow-y:auto;
  box-shadow:inset 0 0 0 1px rgba(var(--ink-rgb),.1);
}
.sheet.is-in .sheet__panel { transform:translate3d(0,0,0) }
.sheet__grip { width:2.25rem; height:.25rem; border-radius:var(--radius-pill);
  background:var(--surface-2); margin:-.5rem auto 1.25rem }
/* Rubriken till vänster, åtgärden till höger — på samma rad.

   Knappen låg på en egen rad under rubriken och läste sig då som det
   första steget i innehållet under. Den gäller hela arket, och det som
   gäller hela arket hör hemma i dess huvud. gap håller dem isär när
   rubriken är lång; knappen krymper aldrig. */
.sheet__head { display:flex; align-items:center; justify-content:space-between; gap:1rem }
.sheet__action { flex:0 0 auto }
.sheet__action[hidden] { display:none }
.sheet__action .btn { margin:0 }
.sheet__title { font-size:1.125rem; font-weight:600; letter-spacing:-.02em }
.sheet__text { margin-top:.5rem; font-size:.875rem; line-height:1.6; color:rgba(var(--ink-rgb),.66) }
/* En växel och det den öppnar, som ett stycke.

   Fälten låg förut som fristående kort under reglaget, och man fick leta
   efter vad som just dök upp. Nu delar de ram: reglaget är rutans huvud
   och fälten fälls ut inuti den.

   Namnet är upptaget av den här och ingenting annat. Installationsgrinden
   längst ned i filen hette också .gate, och eftersom den står senare vann
   den: varje växel i sektionsredigeraren blev en svart position:fixed-yta
   som täckte lådan den låg i. Den heter .installGate nu. Två komponenter
   som inte har med varandra att göra får inte dela klassnamn. */
/* Luft mellan ett ensamt reglage och nästa växel.

   Ett reglage utan följdfält är ett .card och nästa växel en .gate, och
   mellan de två fanns ingen regel alls: korten satt kant i kant medan allt
   annat på skärmen andades. Gaten bär sin luft nedåt, så det är uppåt den
   saknades. */
.card + .gate { margin-top:.75rem }

/* Ett ensamt reglage bär samma avstånd nedåt som ett fält gör.

   Uppmätt i panelen före rättelsen: fält → reglage 24 px, reglage → fält
   0 px. Reglaget är ett kort och kort bär ingen marginal — de får sin av
   .card + .card, och den regeln gäller inte när nästa sak är ett fält.
   Följden var att rubriken under låg klistrad mot reglagets underkant
   medan allt annat i spalten andades lika: det syns som skav långt innan
   man kan säga vad det är.

   Två reglage i följd hör däremot ihop och står kvar på sina .75 rem.
   :has frågar om nästa syskon, vilket är precis den fråga marginalen
   svarar på.

   Regeln gäller överallt nu, också inuti en växel: växeln är inte längre
   en ruta med egna mått, se .gate. */
.card.gate__head { margin-bottom:1.5rem }
.card.gate__head:has(+ .card) { margin-bottom:.75rem }

/* Växeln är struktur, inte en ruta.
 *
 * Reglaget och fälten det öppnar låg förut i en gemensam ram: reglaget
 * som rutans huvud, fälten utfällda inuti. Tanken var att visa att de hör
 * ihop. Utfallet var en ruta inuti en fålla inuti en spalt — tre kanter
 * utanpå varandra för att säga en enda sak — och inuti den låg fälten
 * indragna ett steg till, med sin egen kant. En "Knappar"-fålla såg ut
 * som ett formulär i ett formulär.
 *
 * Sambandet klarar sig utan ram. Fälten dyker upp när reglaget slås på
 * och försvinner när det slås av, och det är den starkaste kopplingen som
 * finns: de finns bara därför att det står på. Kvar här är avståndet
 * nedåt, alltså det enda växeln behöver bidra med.
 *
 * Reglaget bär därmed sitt eget kort igen, som varje annat reglage i
 * appen, och fälten står fritt i spalten som varje annat fält. */
.gate { margin-bottom:1.25rem }
/* Reglaget och det det öppnar står tätare med varandra än med nästa sak
   på skärmen. Utan ram är avståndet det enda som säger att de hör ihop. */
.gate > .card.gate__head { margin-bottom:.75rem }
.gate__body { padding:0 }
.gate__body > .field:last-child { margin-bottom:0 }
.gate__body > .gate:last-child,
.gate__body > .card.gate__head:last-child { margin-bottom:0 }

/* Adressen som en rad: domänen står kvar, slugen skrivs.

   Ramen sitter på raden och inte på fältet, så de två läses som en enda
   adress i stället för som en etikett bredvid ett fält. */
.slugRow { display:flex; align-items:center; min-height:3.25rem;
  border:1px solid var(--line); border-radius:var(--radius-control);
  background:var(--card); padding-left:1rem; transition:border-color .2s;
  overflow:hidden }
.slugRow:focus-within { border-color:rgba(var(--ink-rgb),.35) }
/* Ingen rtl-vändning. Den flyttade snedstrecket till början och gjorde
   "rate.nu/" till "/rate.nu" — rätt tecken, fel ände. */
/* Krymper inte för fältets skull. Med flex:0 1 auto vann inmatningen och
   kortade av även "rate.nu/" till "rate…". Taket finns kvar för riktigt
   långa adresser. */
.slugRow__pre { flex:0 0 auto; max-width:62%; font-size:1rem;
  color:rgba(var(--ink-rgb),.5); white-space:nowrap; overflow:hidden;
  text-overflow:ellipsis }
.slugRow .ctrl { flex:1 1 auto; min-width:0; border:0; background:none;
  padding-left:0; border-radius:0 }
.slugRow .ctrl:focus { border:0 }

/* ---------- mediabiblioteket ---------- */
/* Fota och Ladda upp låg i en egen rad under flikarna. De står bredvid
   rubriken nu, i .wActions, där Skapa ny sida står under Sidor. */

/* Kvadrater i ett rutnät. Bilderna har olika proportioner och ett rutnät
   som följer dem blir en trasa; beskärningen gör listan läsbar och säger
   ingenting om hur bilden används på sidan. */
.mediaGrid { display:grid; grid-template-columns:repeat(3,1fr); gap:.5rem }
/* Kvadraten som padding, inte som aspect-ratio.

   Rutan bar först aspect-ratio och bilden height:100%. Det höll så länge
   rutan var en <figure>, men i bildväljaren är varje ruta en <button> —
   och en knapp är inget vanligt block i Safari: den räknar varken
   aspect-ratio eller klipper sin overflow på samma sätt. Korten blev låga
   lådor med oklippta foton liggande ovanpå varandra, medan samma
   märkspråk såg rätt ut i Chrome.

   Att flytta ratio till bilden — bredd 100 %, höjd auto, aspect-ratio 1 —
   såg ut att räcka, men gjorde det inte: i iOS Safari räknas ratio lika
   illa på bilden inuti knappen, och fotona låg kvar i sina egna
   proportioner ovanpå varandra i väljaren. Biblioteket såg rätt ut hela
   tiden, för där är rutan en <figure>.

   padding-top:100% på en tom ruta ger kvadraten utan att någon behöver
   räkna ratio, och en bild som ligger absolut i rutan kan inte bli högre
   än den. Ingen av delarna beror på hur en knapp råkar behandlas.
   Rundningen ligger på bilden också, så hörnen är runda även där
   knappens klippning inte gäller. */
.mediaCard { position:relative; display:block; margin:0; border:0; padding:0;
  height:0; padding-top:100%;
  border-radius:.75rem; overflow:hidden; background:var(--surface-2); cursor:pointer }
.mediaCard img { position:absolute; inset:0; display:block; width:100%; height:100%;
  object-fit:cover; border-radius:inherit }
.mediaCard__x { position:absolute; top:.25rem; right:.25rem; display:grid;
  place-items:center; width:1.75rem; height:1.75rem; border:0; padding:0;
  border-radius:50%; background:rgba(10,10,10,.55); color:#fff; cursor:pointer }
.mediaCard__x svg { width:.8125rem; height:.8125rem; stroke:currentColor;
  fill:none; stroke-width:1.75; stroke-linecap:round; stroke-linejoin:round }

/* Vad bilden föreställer, längs underkanten.

   Rutan är kvadratisk och bilden fyller den helt, så texten har ingen egen
   plats att ligga på — den läggs därför ovanpå med en tonad botten under
   sig, som en bildtext i ett fotoreportage. Två rader räcker: meningen är
   skriven kort med flit, och den som vill läsa hela ser den i titeln.

   Bara på pekdon som kan hovra. På en telefon fyller texten en fjärdedel
   av varje ruta i ett rutnät man rullar igenom för att känna igen ett
   motiv, och då är den i vägen för det den beskriver. */
.mediaCard__alt {
  position:absolute; left:0; right:0; bottom:0;
  padding:1.5rem .5rem .4375rem;
  background:linear-gradient(to top, rgba(10,10,10,.8), transparent);
  color:#fff; font-size:.6875rem; line-height:1.3;
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;
  overflow:hidden;
  opacity:0; transition:opacity .12s ease;
  pointer-events:none;
}
@media (hover:hover) {
  .mediaCard:hover .mediaCard__alt { opacity:1 }
}

/* Kanalraderna i kopplingskortet: en per ställe inläggen går ut till, med
   namnet på kanalen framför. Utan etiketten stod sidans namn ensamt och
   Instagram som enda ord på skärmen — kortet lästes då som att bara
   Instagram var kopplat. */
.chan { display:flex; align-items:baseline; gap:.75rem; padding:.5rem 0 }
.chan:first-child { padding-top:0 }
.chan + .chan { border-top:1px solid var(--line) }
.chan__k { flex:none; width:5.5rem }
.chan__v {
  flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  font-weight:600; letter-spacing:-.01em;
}
.chan__v--off { font-weight:400; color:rgba(var(--ink-rgb),.62) }

/* Villkorslistan i kopplingskortet.

   Punkterna tillbaka, av samma skäl som i .rte__body: ul { list-style:none }
   är nollställt globalt för appens egna listor, och den här ärvde det. Tre
   krav såg ut som tre indragna stycken, alltså som löpande text — och då
   läser man dem inte som en lista att bocka av. Punkten är dämpad så den
   räknar raderna utan att konkurrera med dem. */
.reqs { margin:0; padding-left:1.05rem; list-style:disc; line-height:1.55 }
.reqs li + li { margin-top:.3125rem }
.reqs li::marker { color:rgba(var(--ink-rgb),.4) }

/* Väljaren i arket rullar i sig själv, som sektionsväljaren. */
.mediaGrid--pick { max-height:min(24rem,48dvh); overflow-y:auto; padding:.125rem }

/* Rullningslisten i lådor som rullar i sig själva.

   Appens vyer gömmer sina listar helt — en list i en app avslöjar att det
   är en webbsida. Men en låda inuti en vy är något annat: där är listen
   det enda som säger att det finns mer att se, och utan den ser rutnätet
   ut att sluta vid nedersta raden. Den ska alltså finnas, men vara tunn,
   rundad och i bläckets ton i stället för webbläsarens grå platta.

   Två skrivsätt behövs: scrollbar-color är standarden (Firefox och nyare
   Chrome), ::-webkit-scrollbar den som fortfarande gäller i Safari.
   Tumgreppet ritas med en genomskinlig kant och background-clip, så att
   det blir smalare än sitt spår utan att spåret behöver en egen färg. */
.scrollThin { scrollbar-width:thin; scrollbar-color:rgba(var(--ink-rgb),.24) transparent }
.scrollThin::-webkit-scrollbar { width:.625rem; height:.625rem }
.scrollThin::-webkit-scrollbar-track { background:transparent }
.scrollThin::-webkit-scrollbar-thumb {
  background:rgba(var(--ink-rgb),.24); border-radius:var(--radius-pill);
  border:3px solid transparent; background-clip:content-box;
}
.scrollThin::-webkit-scrollbar-thumb:hover { background:rgba(var(--ink-rgb),.4); background-clip:content-box }

/* Ett brett ark. Bildväljaren är till för att titta i: på en telefon
   räcker tre spalter över hela bredden, men i en dialog på tjugofyra rem
   vid ett skrivbord blir samma tre spalter tumnaglar man inte kan skilja
   åt. Rutan får då både mer bredd och mer höjd, och spalterna blir fler
   först när de faktiskt har plats. */
@media (min-width:1100px) {
  body.is-app .sheet--wide .sheet__panel { width:min(52rem, calc(100vw - 6rem)) }
  body.is-app .sheet--wide .mediaGrid--pick {
    grid-template-columns:repeat(5, 1fr);
    max-height:min(34rem, 60dvh);
    gap:.75rem;
  }
}

/* Förhandsbilden i rutan med AI-valen. */
.imgPrev { border-radius:1rem; overflow:hidden; background:var(--surface-2);
  aspect-ratio:16/10 }
.imgPrev img { width:100%; height:100%; object-fit:cover; display:block }

.sheet__body { margin-top:1.25rem }

/* Rutnät av sektionstyper.

   Panelen rullar redan, men bara till 88 % av skärmen. När typerna blir
   fler än så ska rutnätet rulla i sig självt, så att rubriken och
   Avbryt-knappen står kvar medan man letar. */
/* Sektionsväljarens kort. Rullar i sig självt så rubriken och Avbryt
   står kvar medan man letar. */
.pickcards { max-height:min(28rem,54dvh); overflow-y:auto; padding:.125rem }

/* Rullningslisten i väljarna.

   Webbläsarens egen är bred, grå och ritar en ram tvärs genom rutnätet.
   Den här är en tumme utan ränna: samma greppyta, men den syns bara som
   det den är — en markör för hur långt ned man är. Bredden ligger kvar
   hela tiden så korten inte hoppar i sidled när listen tonar fram. */
.pickcards, .pickgrid { scrollbar-width:thin; scrollbar-color:rgba(var(--ink-rgb),.24) transparent }
.pickcards::-webkit-scrollbar, .pickgrid::-webkit-scrollbar { width:10px }
.pickcards::-webkit-scrollbar-track, .pickgrid::-webkit-scrollbar-track { background:transparent }
.pickcards::-webkit-scrollbar-thumb, .pickgrid::-webkit-scrollbar-thumb {
  background:rgba(var(--ink-rgb),.2); background-clip:content-box;
  border:3px solid transparent; border-radius:999px; transition:background .15s }
.pickcards:hover::-webkit-scrollbar-thumb, .pickgrid:hover::-webkit-scrollbar-thumb {
  background:rgba(var(--ink-rgb),.34); background-clip:content-box }

.pickgrid { display:grid; grid-template-columns:repeat(2,1fr); gap:.625rem;
  max-height:min(26rem,52dvh); overflow-y:auto; padding:.125rem }
.pickgrid__t { display:grid; place-items:center; text-align:center;
  min-height:4.5rem; padding:.875rem .625rem; border:1px solid var(--line);
  border-radius:.875rem; background:var(--background); cursor:pointer;
  font:inherit; font-size:.875rem; font-weight:600; letter-spacing:-.01em;
  line-height:1.3; color:var(--foreground); transition:background .15s, border-color .15s }
.pickgrid__t:active { background:var(--surface-2); border-color:rgba(var(--ink-rgb),.3) }
@media (min-width:26rem) { .pickgrid { grid-template-columns:repeat(3,1fr) } }
.sheet__actions { margin-top:1.25rem; display:flex; flex-direction:column; gap:.625rem }

/* ============================== laddning ========================== */
.boot { position:fixed; inset:0; z-index:500; display:grid; place-items:center;
  background:var(--ink); transition:opacity .3s }
.boot.is-gone { opacity:0; pointer-events:none }
.boot svg { width:8rem; height:auto }

.rowSplit { display:flex; align-items:center; justify-content:space-between; gap:1rem }
.divider { height:1px; background:var(--line); margin:1.25rem 0 }
.spacer { height:1rem }

/* ============================ telefonram ==========================
   Under utveckling öppnas appen på en laptop, och ett mobilgränssnitt
   utsträckt över en hel skärm ger fel känsla för allt man bedömer:
   radlängder, hur långt ner en knapp hamnar, hur mycket som ryms utan
   scroll. Därför ritas appen i en ram med iPhone 12 Pro:s proportioner
   (390 × 844 pt) så snart fönstret är stort nog.

   På telefon är #app display:contents och finns alltså inte som box —
   ingen av reglerna här gäller, och layouten är oförändrad. */
#app { display:contents }

/* ---------- telefonen: #app är skärmen ----------
   Ett mätt fall, iPhone 12 Pro, appen på hemskärmen:

     skärmen                      844,0 pt
     100dvh, och det fönster
     position:fixed mäts mot      797,3 pt
     env(safe-area-inset-top)      47,0 pt
     env(safe-area-inset-bottom)   34,0 pt

   Sidan börjar vid noll — den ritas under statusraden, det är vad
   viewport-fit=cover och black-translucent tillsammans betyder — men
   fönstret slutar 46,7 pt för tidigt. Det är statusradens höjd, räknad en
   gång för mycket: iOS drar av den i underkanten i stället för i
   överkanten. Allt som hängde i bottenkanten hamnade därför en statusrad
   för högt: flikraden slutade mitt på skärmen med en död remsa under sig,
   och inloggningen klipptes av på samma ställe.

   Att lappa varje lager för sig blev fel varje gång — det finns sju av
   dem, och de mäter mot samma trasiga kant. Här får de i stället en egen
   kant att mäta mot. #app blir en box som täcker hela skärmen, och en
   transform gör den till containing block för alla position:fixed inuti.
   Då gäller inset:0, bottom:0 och env(safe-area-inset-bottom) precis som
   de läser, och skrivbordets telefonram — som gjort exakt så här hela
   tiden — och telefonen har äntligen samma geometri.

   --brist är skillnaden, mätt i JS mot screen.height. Den är noll överallt
   utom i den installerade appen på iOS, och noll är också det enda den
   någonsin blir om måttet ser orimligt ut. */
@media (max-width:759px), (max-height:639px) {
  #app {
    display:block;
    position:fixed; top:0; left:0; right:0;
    /* 100vh och inte 100dvh, och det är hela poängen.

       I den installerade appen på iOS svarar de två olika: dvh ger
       fönstret som statusraden redan är avdragen ur — 797 punkter på en
       iPhone 12 Pro — medan vh ger skärmen, 844. Det är vh som är sant
       här. dvh finns för att följa en webbläsarlist som kommer och går,
       och någon sådan list har en app på hemskärmen inte.

       Med dvh slutade allt bottenförankrat 47 punkter för tidigt: menyn
       mitt på skärmen med en död remsa under, och inloggningen avklippt
       på samma ställe. */
    height:100vh;
    transform:translateZ(0);
  }
}

/* Förhandsgranskningen hör till skrivbordslayouten och finns inte annars.
   Den ligger ändå i märkspråket, för att slippa bygga och riva ett element
   vid varje ändring av fönstret. */
.deskPrev { display:none }

/* Telefonramen på en stor skärm.

   Den gäller inloggningen och välkomstvyerna, som är byggda för en smal
   spalt och ser ut som en tavla mitt i ett tomt fönster utan den.

   Den gäller inte appen. Där fanns ett läge mellan 760 och 1100 pixlar
   där arbetet låg i en attrapp av en telefon på en skrivbordsskärm: för
   litet för att arbeta i, och en bild av något annat än det man höll på
   med. Under 1100 är appen i stället telefonens layout i hela fönstret,
   och över 1100 är den skrivbordets. */
@media (min-width:760px) and (min-height:640px) {
  html:has(body:not(.is-app)) { background:var(--surface-2) }
  body:not(.is-app) { display:grid; place-items:center; min-height:100dvh; padding:1.5rem 0 }

  body:not(.is-app) #app {
    display:block;
    position:relative;
    /* Höjden styr, bredden följer av proportionen. Ramen krymper med
       fönstret i stället för att klippas. */
    height:min(844px, calc(100dvh - 3rem));
    aspect-ratio:390 / 844;
    border-radius:2.75rem;
    overflow:hidden;
    isolation:isolate;
    /* transform gör #app till containing block för position:fixed inuti.
       Utan den skulle flikrad, dialog och notiser lägga sig mot fönstret
       och hamna utanför ramen. */
    transform:translateZ(0);
    box-shadow:0 0 0 .5rem #14100c, 0 0 0 .5625rem rgba(255,255,255,.12),
               0 1.75rem 3.5rem rgba(10,10,10,.22);
  }

  /* Statusradens område. Appen reserverar redan plats via env(safe-area-inset-top),
     som är 0 i en webbläsare — här ges den ett värde så toppfältet hamnar
     där det gör på en riktig telefon. */
  body:not(.is-app) #app { --top:.75rem }

  /* Skärmarna ärver ramens rundning i hörnen. */
  body:not(.is-app) .screen, body:not(.is-app) .boot { border-radius:inherit }
}


/* ============================== skrivbord =========================
   Telefonramen är en nödlösning: den visar appen på en dator, men den
   använder inte datorn. Över 1100 px lämnar appen därför ramen.

   Layouten har två lägen, inte tre spalter.

   Menyn står till vänster, som i vilket skrivbordsprogram som helst, och
   är kvar hela tiden.

   I det första står man i en flikvy — Hem, Hemsida, Inkorg, Chatt. Den
   ligger som en bred spalt mitt i ytan bredvid menyn. Ingenting annat: en
   lista över sidor behöver inte en hemsida bredvid sig för att vara
   begriplig.

   I det andra har man öppnat något. Då läggs ett lager över alltihop:
   hemsidan fyller ytan och det man redigerar ligger i en låda till
   höger. Går man en nivå till — en sektion inuti en sida — glider nästa
   låda in på samma plats, precis som på telefon.

   Allt ligger i en egen mediefråga. Under 1100 px rörs ingenting. */
@media (min-width:1100px) {
  /* Bordet som telefonramen står på. Grått bara när det FINNS en ram:
     inloggad fyller appen hela fönstret, och sedan flikvyerna rullar med
     dokumentet är sidan högre än #app — det grå tittade fram under den
     som en remsa i fel färg. Rotelementet målar duken, så färgen måste
     rättas här och inte på body: bodys bakgrund propagerar till duken
     bara när roten saknar egen. */
  html { background:var(--surface-2) }
  html:has(> body.is-app) { background:var(--background) }

  /* Under uppstarten vet appen ännu inte om någon är inloggad, och alltså
     inte heller vilken layout som gäller. Telefonramen först och ett byte
     en halv sekund senare ser trasigt ut — hellre bara logotypen mot
     bakgrunden tills det är avgjort. */
  body:has(.boot:not(.is-gone)) #app {
    display:block; width:100%; height:100dvh;
    aspect-ratio:auto; border-radius:0; box-shadow:none;
  }

  /* Bara inloggat läge. Välkomst- och inloggningsvyerna har ingen hemsida
     att visa bredvid sig och får behålla telefonramen. */
  body.is-app { display:block; min-height:0; padding:0 }

  body.is-app #app {
    --nav:14rem;       /* menyn till vänster */
    /* Listen överst. Högre på skrivbordet än de 3 rem telefonen ärver:
       här bär den knappar och inte bara text, och en knapp på 2,25 rem i
       en list på 3 rem lämnar sex pixlar över och under — för lite för
       att listen ska läsas som en list. Fyra rem ger fjorton. Den var
       fem när menyn stod i den; med menyn i spalten till vänster bär den
       bara hörnets knappar, och då är fem en tom remsa. Tre och en halv
       nu, sedan projektväljaren flyttade in i menyn: kvar i listen är
       klockan, kontot och uppmaningen om domänen, och de är 2,25 rem.
       Menyns väljare mäter mot samma variabel, och vyerna som börjar
       under listen likaså. */
    --topbar:3.5rem;
    /* Rubrikhuvudets höjd: stoppningen uppe, rubriken, kontrollraden och
       stoppningen ned. Fast tal för att huvudet är fast — sökfältet visas
       alltid på skrivbordet, så raden byter aldrig höjd.

       Den bor här som variabel och inte som ett tal i tre regler, för tre
       saker mäter mot den: flikradens klistring, listkortets minsta höjd
       och tomrutans. På en låg skärm krymper huvudet, och när talet stod
       skrivet i regeln som klistrar följde den inte med — kvar blev en
       genomskinlig remsa på 32 px mellan rubriken och kortet, som listan
       rullade fram i. Ett mått, ett ställe. */
    /* Huvudets höjd: 24 px stoppning + 66 px innehåll. Se
       .screenHead nedan — de två måste ändras tillsammans. */
    --headH:5.625rem;
    /* Vad kortet ska nå ned till. Fönstret minus listen, huvudet,
       kortets egen flikrad (3,75 rem) och luften ned till kanten (1,5).
       Summan var skriven som "19rem": rätt på en stor skärm, och fel så
       fort någon av delarna ändrade sig. Nu räknas den. */
    --kortH:calc(100dvh - var(--topbar) - var(--headH) - 5.25rem);
    --tabbar:0px;      /* ingen flikrad nedtill: menyn står till vänster */
    /* Måste sättas om här också, inte bara --tabbar: den är redan
       utbytt och ärvs ned som färdig text. Se :root. */
    --tabbarTotal:var(--zonNed);
    --spalt:54.5rem;   /* flikvyn */
    /* Assistentens läsbredd. Talet var menypillrets bredd när menyn
       stod i listen, och spalten mättes mot det; pillret är borta men
       måttet visade sig vara det rätta för ett samtal — en rad man
       läser utan att vrida på huvudet. Se #screen-dashboard nedan. */
    --meny:42.75rem;
    --drawer:28rem;    /* lådan: något bredare, den bär formulär */
    --top:0px;         /* ingen statusrad att hålla undan för */

    display:block; position:relative;
    /* min-height och inte height: taket blir ett golv. Flikvyerna hänger
       absolut placerade i den här behållaren, och en absolut placerad låda
       som är högre än sin behållare växer dokumentets rullyta i stället för
       att kapas — det är så hela sidan börjar rulla i stället för rutan. */
    width:100%; min-height:100dvh;
    aspect-ratio:auto;
    border-radius:0; box-shadow:none;
    /* En enda bakgrundsfärg i hela fönstret. Spalten ritade förut sin egen
       ruta med kanter, och den rutan sa ingenting som inte bredden redan
       sa. Kvar är en linje: den mellan menyn och arbetet. */
    background:var(--background);
  }

  /* Menyn får växa med fönstret. 16 rem är rätt när spalten och menyn
     tillsammans fyller bredden; på en riktigt bred skärm blir den en smal
     remsa i kanten medan ytan bredvid står tom, och raderna ser trängda
     ut fast de har lika mycket luft som förut. */
  @media (min-width:1500px) { body.is-app #app { --nav:15rem } }
  @media (min-width:1800px) { body.is-app #app { --nav:16rem } }


  /* Utanför ett projekt finns varken meny eller list att göra plats åt. */
  body.is-app:not(.has-project) #app { --nav:0px; --topbar:0px }
  body.is-app:not(.has-project) .topbar { display:none }

  body.is-app .screen, body.is-app .boot { border-radius:0 }

  /* ---- flikvyn: en bred spalt mitt på skärmen ----
     Allt börjar under listen. Den gäller hela projektet och ska inte
     kunna hamna under en vy som glider in. */
  body.is-app .screen {
    top:var(--topbar);
    left:calc(var(--nav) + (100% - var(--nav) - var(--spalt)) / 2);
    right:auto; width:var(--spalt);
    border-left:0; border-right:0;
  }

  /* Projektväljaren öppnad inifrån ett projekt tar hela fönstret.

     Som spalt mellan menyn och listen såg den ut som ännu en vy i det
     projekt man just ville lämna — menyn till vänster visade fortfarande
     dess sidor och listen dess namn, medan innehållet handlade om något
     annat. Ett byte av projekt är ett byte av allt, och då ska ingenting
     av det gamla stå kvar runt omkring.

     58 ligger över listen (46) och över den modala lådans täckare (55). */
  body.is-app #screen-projects.is-over {
    top:0; left:0; right:0; width:auto; z-index:58;
  }
  body.is-app #screen-projects.is-over .lobbyBack { top:1rem }

  /* Och gömd resten av tiden.

     Routern håller hela föräldrakedjan aktiv med flit, så projektväljaren
     står kvar "aktiv" bakom flikvyerna. På telefonen är det rätt: vyerna
     glider in ÖVER den, och det som skymtar under kanten under rörelsen
     ska vara lobbyn man kom ifrån.

     På skrivbordet är förhållandet ett annat. Flikvyn är absolut placerad
     och rullar med sidan; väljaren är fastnålad och står stilla. Rullar
     man ned glider spalten uppåt och lobbyn kommer fram under den — två
     skärmar staplade på varandra, med projektlistan och Logga ut mitt i
     det man höll på med.

     Det lagades förut bara för Assistenten, genom att binda den vyns
     underkant (se #screen-dashboard nedan). De övriga fyra ska däremot få
     växa med sitt innehåll — en lista ska kunna bli hur lång som helst —
     så där är det lobbyn som ska bort i stället för vyns rullning.

     has-project säger att ett projekt är öppet, is-over att man faktiskt
     står i väljaren. Utan öppet projekt är lobbyn själva vyn och rörs
     inte. */
  body.is-app.has-project #screen-projects:not(.is-over) { display:none }

  /* De fyra flikvyerna är push-vyer i routern — på telefon glider de in
     över projektlistan. Här är de själva arbetet och ligger i spalten.
     De sänks också ett steg i höjd, så lagret kan läggas över dem. */
  body.is-app .screen--tab {
    top:var(--topbar);
    left:calc(var(--nav) + (100% - var(--nav) - var(--spalt)) / 2);
    right:auto; width:var(--spalt);
    z-index:10;
    border-left:0; border-right:0;
    box-shadow:none;
  }

  /* Luften överst hör till huvudet, inte till skärmen.

     Ett klistrat huvud fäster mot innehållskanten och inte mot skärmens
     överkant: skärmens egna 1,25 rem lämnade alltså en remsa där listan
     rullade förbi ovanför rubriken. Nu bär huvudet avståndet självt och
     täcker hela vägen upp. */
  /* Luften över rubriken hör till skrivbordet. Där står vyn i en spalt
     mitt på en stor yta och behöver en marginal uppåt för att inte se
     fastklistrad i topplisten ut. På telefon är skärmen hela ytan, och
     samma luft blev bara en tom remsa som knuffade listan nedåt. */
  /* Luft under listen.

     Stoppningen var 4 px. Den sattes när listen inte hade någon linje —
     då flöt rubriken bara ut ur en yta i samma färg, och att den började
     högt upp läste sig som att den var dokumentets första rad.

     Med linjen i listens underkant är det inte längre sant. Linjen säger
     "här slutar ramen", och en rubrik som börjar 4 px under den ser
     fastklistrad ut i kanten i stället för placerad under den. Ett dryga
     halvt radavstånd räcker för att den ska läsa som att den står PÅ
     ytan och inte klistrad mot taket.

     --headH nedan ÄR huvudets höjd och måste ändras lika mycket. Allt
     klistrat mäter mot den: tabellhuvudet står på
     calc(var(--topbar) + var(--headH)), och kortets minsta höjd räknas
     ur den. Ändras bara stoppningen glider tabellhuvudet in under
     rubriken. Uppmätt: huvudet var 70 px med 4 px stoppning, alltså
     66 px innehåll — 66 + 24 = 90 px = 5,625 rem. */
  body.is-app .screen--tab .screenHead { padding-top:1.5rem }
  /* Rubriken bär vyns namn på en yta som är dubbelt så bred som en
     telefons. Två rem läste sig som en underrubrik där. */
  /* Inte > .h1: på listvyerna ligger rubriken i en .rowSplit tillsammans
     med sök och skapa, alltså ett steg ned. */
  body.is-app .screen--tab .screenHead .h1,
  body.is-app #screen-dashboard .bar__homeWord { font-size:2.5rem }
  /* Toppmarginalen bort. .screenHead > .h1 bär .5 rem, men bara när
     rubriken står direkt i huvudet — i listvyerna ligger den i en .rowSplit
     och gör det inte. Följden var att Inkorg och Statistik stod åtta pixlar
     lägre än Hemsida, och Assistent på en tredje höjd. Nu bär stoppningen
     hela avståndet och rubrikerna möts. */
  body.is-app .screen--tab .screenHead .h1 { margin-top:0 }

  /* ---- rullningen hör till sidan, inte till spalten ----

     Vyerna ärver position:fixed med inset:0 från telefonen: en låda så hög
     som fönstret, med sin egen rullning inuti. På en telefon är det rätt —
     där ÄR lådan skärmen. På skrivbordet blev den en ruta mitt i ett stort
     fönster med en egen rullningslist, medan sidan runt omkring stod still.

     Absolut och inte fixerad: fixerade lådor räknas inte in i dokumentets
     rullyta, och sidan hade fortsatt stå still hur hög vyn än blev.

     bottom:auto måste skrivas ut. inset:0 satte den till noll, och en låda
     med både top och bottom bundna har sin höjd bestämd — height:auto blir
     verkningslöst så länge underkanten sitter fast. */
  body.is-app .screen--tab {
    position:absolute; bottom:auto;
    height:auto; min-height:calc(100dvh - var(--topbar));
    overflow:visible;
  }

  /* Bara den flik man står i tar plats.
   *
   * De fem flikvyerna är push-vyer i routern, och .screen--push bär
   * display:flex hela tiden — de göms genom att skjutas ut ur bild med
   * transform, inte genom att tas bort. På telefonen är det hela
   * mekaniken: vyerna ligger utlagda och glider in över varandra.
   *
   * Här är de i stället absolut placerade med height:auto, och en
   * utlagd låda växer dokumentets rullyta oavsett var transformen
   * lägger den. Följden: sidlistan med trettio rader gav en rullning på
   * fjorton hundra pixlar — på Assistenten, som inte har något att rulla
   * i. Uppmätt i ett prov med samma stilmall: fönster 945, dokument
   * 2379.
   *
   * Rörelsen förloras inte. Flikbytet på skrivbordet är ögonblickligt
   * (se is-instant i activate), så det finns ingen inglidning att spara
   * — och lådorna som faktiskt glider in är fixerade och rörs inte av
   * den här raden. */
  body.is-app .screen--tab:not(.is-active) { display:none }

  /* Det klistrade huvudet mäter mot fönstret nu och inte mot vyns egen
     rullning, och fönstrets överkant ligger bakom topplisten. */
  body.is-app .screen--tab .screenHead { top:var(--topbar) }

  /* Assistenten är ett samtal och inte ett dokument.

     De andra flikvyerna växer med sitt innehåll och låter sidan rulla —
     rätt för en lista, som ska kunna bli hur lång som helst. Ett samtal
     ska tvärtom ha en fast ram: flödet rullar inuti sig självt och
     skrivfältet står still längst ned, där man skriver.

     Utan ramen växte vyn med varje replik. Sidan fick en lång rullning,
     skrivfältet åkte uppåt med flödet — klistringen nedan höll bara tills
     fältets egen plats kom i bild — och under vyns underkant syntes
     projektlistan, som ligger kvar fastnålad bakom flikvyerna på
     skrivbordet. Det såg ut som två skärmar staplade på varandra.

     min-height nollställs med flit: .screen--tab sätter 100dvh minus
     listen, och en ram med både top och bottom bundna behöver ingen. */
  body.is-app #screen-dashboard {
    position:fixed; bottom:0; height:auto; min-height:0; overflow:hidden;
  }

  /* Raden rymmer sina chips på den här bredden, så den behöver inte
     rulla. */
  body.is-app #dashboard-body .llm__above { overflow:visible }

  /* Skapa-knappen i listens kontrollrad, på skrivbordet.

     Samma knapp som Koppla domän uppe i listen: samma mått, samma piller,
     samma färg. De är två av samma sort — den enda åtgärden i sin rad —
     och när de såg olika ut lästes de som två olika sorters kontroll.

     Den var fylld i accentfärgen och 2,75 rem hög med mjukt hörn, alltså
     större och tyngre än allt annat på skärmen. Accenten är dessutom bara
     läsbar mot vitt: mot en mörk yta blev den en brun platta med vit text
     som drog blicken från rubriken bredvid.

     Telefonen rörs inte. Där är knappen redan bara ett plustecken under
     30 rem, och över den brytpunkten är skärmen fortfarande en telefon där
     en tydlig knapp är rätt. */
  body.is-app .wActions .btn {
    /* Måtten är .btn--sm:s, samma som listens knapp. Minsta bredden är
       kvar men räknad för den mindre graden: "Nytt inlägg" med sitt
       plustecken mäter 108 px och "Ny sida" 86, och utan golv hoppar
       knappen i sidled när man byter flik. */
    min-height:2.5rem; min-width:8rem;
    padding:.5rem 1.125rem; font-size:.8125rem;
    /* Centrerat, som varje annan knapp i appen. Space-between hörde till
       den breda accentknappen — med orden i mitten behövs det inte. */
    justify-content:center; text-align:center;
    border-radius:var(--radius-pill);
    background:var(--primary); color:var(--on-primary);
    box-shadow:none;
  }
  body.is-app .wActions .btn:hover { background:var(--primary); opacity:.85 }
  /* Fältet bredvid följer med: samma höjd och samma pillerform, annars
     står en rundad ruta intill ett piller och raden ser hopsatt ut. */
  body.is-app .wActions .ctrl--search {
    min-height:2.5rem; border-radius:var(--radius-pill);
  }
  body.is-app .wActions .btn__ico { margin:0 }
  /* Kvadraten följer pillrets höjd här: 2,5 rem, inte telefonens 2,75 —
     och konturen i stället för fyllningen, som regeln ovanför ger alla
     knappar i raden. */
  body.is-app .wActions .wGear {
    width:2.5rem; min-width:0; padding:0;
    background:transparent; color:var(--foreground);
    box-shadow:inset 0 0 0 1px rgba(var(--ink-rgb),.35);
  }
  body.is-app .wActions .wGear:hover {
    background:rgba(var(--ink-rgb),.045); opacity:1;
    box-shadow:inset 0 0 0 1px rgba(var(--ink-rgb),.6);
  }

  /* Flikraden i vyn — Sidor och Inlägg, Förslag och Godkända — högre här.
     Måtten är telefonens, där en kontroll ska ta så lite höjd som möjligt
     av en kort skärm. På en bred vy under en rubrik i 32 px läser sig
     samma rad som hoptryckt. */
  /* Kortets huvud bär radernas egen stoppning, se .seg--card. Undantaget
     måste stå här: den här regeln väger tyngre än klassen ensam. */
  body.is-app .screen--tab .seg--tabs:not(.seg--card) { padding:.375rem }

  /* Flikraden följer med när listan rullar.

     Den ligger utanför det klistrade huvudet — den hör till kortet, inte
     till rubriken — och måste därför klistra själv, precis under huvudet.
     8,625 rem är huvudets höjd i den här vyn: rubrik i 2,5 rem plus
     kontrollraden på 2,75, med sin stoppning. Måttet är fast för att
     huvudet är det: sökfältet visas alltid på skrivbordet, så raden byter
     aldrig höjd.

     z-index under huvudets 6, så rubriken passerar ovanpå flikarna och
     inte tvärtom. */
  body.is-app .screen--tab .seg--card {
    position:sticky; top:calc(var(--topbar) + var(--headH)); z-index:5;
    /* Ytan och ramen flyttar båda till ::after, se nedan.

       border:0 och inte border-color:transparent. En genomskinlig ram tar
       fortfarande plats: ::after ligger mot paddingboxen och hamnade
       därmed 1 px innanför på varje sida, så den svarta botten lyste
       igenom som ett hårstreck runt raden — och den synliga ytan blev
       2 px smalare än listan under. */
    background:none; border:0;
  }

  /* Hörnkilen.

     Flikraden är klistrad och har rundade överhörn. Rundningen skär bort
     två kilar ur radens rektangel — innanför rutan, utanför kanten — och
     där låg ingenting. Listan som rullade förbi UNDER raden syntes alltså
     i de två hörnen: bildrutorna gled upp och tittade fram runt
     rundningen innan de försvann.

     Två lager i stället för ett. ::before är en fyrkantig botten i sidans
     egen färg som täcker hela rutan inklusive kilarna, ::after är den
     rundade ytan ovanpå. Det som rullar förbi möter alltså sidans
     bakgrund och inte tomrum. */
  body.is-app .screen--tab .seg--card::before,
  body.is-app .screen--tab .seg--card::after {
    content:''; position:absolute; pointer-events:none;
  }
  body.is-app .screen--tab .seg--card::before {
    /* Inte under raden. Med -1px runt om målade botten en svart hårlinje
       över listans överkant, alltså en skarv mitt i kortet — precis det
       den skulle dölja. Uppåt och åt sidorna behövs marginalen för att
       täcka ramens egen rad. */
    inset:-1px -1px 0; background:var(--background);
  }
  body.is-app .screen--tab .seg--card::after {
    inset:0; background:var(--card);
    border:1px solid var(--line); border-bottom:0;
    border-radius:var(--radius-control) var(--radius-control) 0 0;
  }
  /* Knapparna ligger över båda lagren. Utan det målas de av de absolut
     placerade pseudoelementen, som annars kommer sist i ordningen. */
  body.is-app .screen--tab .seg--card > * { position:relative; z-index:1 }
  body.is-app .screen--tab .seg--tabs button { height:2.5rem; font-size:.9375rem }
  /* Undervalet i statistiken står utanför de två reglerna ovanför.
     Stoppningen på .375 rem lyfte knapparna från radens underkant, och
     då hamnade den aktiva flikens streck en bit ovanför linjen — två
     streck under varandra, som om raden hade en dubbel botten. Utan
     stoppning ligger strecket på linjen igen, som på telefonen. Texten
     ett snäpp under flikradens: det är ett underval. */
  body.is-app .screen--tab .seg.seg--tabs.statSub { padding:0 }
  body.is-app .screen--tab .seg.seg--tabs.statSub button { height:2.5rem; font-size:.875rem }
  /* Kortets huvud var 1 rem runt om kring en 2,75 rem hög knapp — 78 px
     för en rad med tre ord. Neddraget till .625 rem och 2,5 rem blir raden
     60 px och står närmare det den är huvud för. */
  body.is-app .screen--tab .seg--card { padding:.625rem }

  /* Listan når ned mot fönstrets nederkant även när den är kort.

     Tre rader i ett kort som slutar en tredjedel ned på en stor skärm
     läses som att något saknas — sidan ser halvfärdig ut fast listan är
     komplett. Kortet får en minsta höjd i stället, och tomrummet hamnar
     inuti det där det läses som plats att fylla.

     Måttet räknas från fönstret och inte som ett fast tal: --kortH är vad
     som blir kvar när listen, rubrikraden, flikraden och luften ned till
     kanten är avdragna. Se variabeln.

     Exakt och inte ungefär: 19 rem var en avrundning uppåt, och de pixlar
     den missade blev en rullning på varenda skärm i appen — ett dokument
     14 px högre än fönstret, med ingenting att rulla till. */
  body.is-app .screen--tab .ptableWrap { min-height:var(--kortH) }

  /* Publicera-knappen tar sin plats i flödet.

     Den är klistrad mot underkanten och syns alltid, men den ligger också
     kvar i flödet under listan — och kortet under den räknade inte med
     den. Följden var att Sidor och Inlägg rullade fem rem i onödan: man
     drog nedåt, kortet flyttade sig, och under det låg samma knapp som
     redan stod still i kanten.

     ~-kombinatorn och inte +: mellan kortet och knappen kan det stå en
     tomruta. */
  body.is-app .screen--tab .ptableWrap:has(~ .stickyAction) {
    /* 5,75 rem: bandet är 80 px högt och bär .75 rem marginal ovanför. */
    min-height:calc(var(--kortH) - 5.75rem);
  }

  /* Tomrutan är kortets nedre halva, inte en egen ruta.

     Listan får sitt kort genom .ptableWrap, men en tom flik ritar en
     .empty i stället — och den bar sin egen rundning runt om, sin egen
     streckade kant och ingen minsta höjd. Följden på en bred skärm: en
     flikrad, en skarv, och en liten låda som slutade en tredjedel ned med
     resten av skärmen tom under. Två rutor där det ska vara ett kort.

     Samma behandling som listan får: fyrkantiga överhörn, ingen överkant,
     samma heldragna ram och samma minsta höjd. Innehållet centreras i
     ytan, för nu ÄR ytan hela kortet. */
  body.is-app .screen--tab .seg.seg--card + .empty,
  body.is-app .screen--tab .seg.seg--card + .placeholder,
  body.is-app .screen--tab #inbox-list > .empty,
  body.is-app .screen--tab #website-list > .empty {
    display:flex; flex-direction:column; align-items:center; justify-content:center;
    min-height:var(--kortH);
    border:1px solid var(--line); border-top:0;
    border-radius:0 0 1rem 1rem;
    background:var(--card);
  }

  /* ---- låg skärm ----

     Måtten ovan är satta för en stor bildskärm och mäts alla i bredd. En
     bärbar 13-tummare är lika bred men mycket lägre: kring 790 px
     användbar höjd mot 1200 och uppåt på en yttre skärm. Där kostar samma
     mått en femtedel av vyn innan innehållet ens börjar — listen fem rem,
     rubrikluften fyra och en halv, rubriken två och en halv. Generös luft
     på en stor skärm blir trängsel på en liten, för det som trängs ihop är
     det man kom för att läsa.

     Höjd och inte bredd i frågan: en smal och hög vy har inte problemet,
     en bred och låg har det. Bredden lämnas orörd.

     Blocket står SIST i mediefrågan med flit. Reglerna det skriver om har
     samma specificitet, och då avgör ordningen — placerat längre upp vann
     originalen och bara --topbar slog igenom. */
  @media (max-height:900px) {
    /* Blocket ska SPARA höjd, och gjorde motsatsen.

       Det skrevs när listen var 5 rem och stoppningen 4,5: då var 4 och
       2 rem en krympning. Båda förvalen har sänkts sedan dess — listen
       till 3,5 rem, stoppningen till 1,5 — men blocket stod kvar med
       sina gamla siffror. På en låg skärm blev listen alltså en halv rem
       HÖGRE och luften över rubriken en halv rem STÖRRE än på en hög.
       Precis tvärtemot vad hela mediefrågan finns till för.

       Listen rörs inte längre: 3,5 rem är redan så låg den blir. Det som
       sparas är stoppningen, från 1,5 till 0,75 rem, och rubriken från
       2,5 till 2 — den senare var och förblir en riktig besparing.

       --headH uppmätt, inte räknad: 78 px = 4,875 rem. Den mindre
       rubriken krymper inte raden så mycket som man skulle tro — på
       listvyerna står rubriken i en .rowSplit tillsammans med sökfältet
       och Ny sida, och de två bestämmer radens höjd. Besparingen sitter
       alltså i stoppningen, inte i graden. */
    body.is-app #app { --headH:4.875rem }

    body.is-app .screen--tab .screenHead { padding-top:.75rem }
    /* Assistentens list i par med huvudet, se .bar-regeln ovan. Utan den
       raden krympte bara de andra flikarna och Assistent blev den enda
       vyn med full luft kvar. */
    body.is-app #screen-dashboard .bar { padding-top:.75rem }
    body.is-app .screen--tab .screenHead .h1,
    body.is-app #screen-dashboard .bar__homeWord { font-size:2rem }
  }
  /* Linjen under sista raden står kvar här. Utan minsta höjd slutar kortet
     där listan slutar, och då är linjen en kant ovanpå en kant. Med den
     ligger sista raden mitt i en tom yta, och strecket säger var listan
     tar slut i stället för att lämna den svävande. */
  body.is-app .screen--tab .ptableWrap .prow:last-child {
    border-bottom:1px solid var(--line);
  }
  /* Sökfältet står bredvid och ska inte vara högre än knappen.

     min-height räcker inte: .ctrl bär .9375 rem stoppning uppe och nere,
     och 15 + 15 + textraden blev 45 px oavsett vad minsta höjden sa. Det
     är stoppningen som måste ge med sig. */
  body.is-app .wActions .ctrl--search {
    min-height:2.75rem; padding-top:.6875rem; padding-bottom:.6875rem;
    font-size:.9375rem;
    /* Luppen närmare kanten. En rem var räknat för ett fält på 2,75 rem;
       i ett på 2,25 står ikonen för långt in och lämnar ett hål mellan
       kanten och sig själv. Stoppningen följer med, annars börjar texten
       där den gjorde. */
    padding-left:2.375rem;
    background-position:left .75rem center;
  }

  /* Ingen toning ovanför fältet.

     Den fanns för att flödet rullade UNDER fältet och en textrad som tog
     tvärt slut mot kanten läses som avklippt. Nu rullar sidan i stället,
     och remsan blev en halvt bortsuddad mening ovanför rutan — den ser ut
     som ett fel, inte som en fortsättning. Samma skäl som .llm--float. */
  body.is-app #dashboard-body .llm__bar::before { display:none }


  /* ---- lådan: det man öppnat ---- */
  /* Bakåtpilen hör till telefonen, där en vy ligger ovanpå den förra och
     en pil är enda vägen tillbaka. På skrivbordet är lådan en låda: den
     stängs genom att man trycker utanför den, och pilen blev en knapp som
     upprepade det ytan redan gjorde — samtidigt som den tvingade rubriken
     till mitten för att hålla listen i balans.

     Utan pilen är listen två saker: rubriken till vänster, där man läser,
     och kuggen till höger, om vyn har en. */
  /* Lådans egen bakåtpil, alltid.

     Den var dold utom när lådan stod på en annan låda: så länge krysset
     fanns var det ett steg tillbaka för en låda öppnad ur en lista, och
     två knappar bredvid varandra hade sagt samma sak. Krysset är borta,
     och då är pilen den enda vägen ut ur lådan — och en väg ut som ibland
     finns och ibland inte är sämre än ingen alls. Den tar ett steg,
     varje gång, oavsett hur djupt man står. */
  body.is-app .screen--push:not(.screen--tab) .bar {
    grid-template-columns:2.75rem 1fr 2.75rem;
  }

  body.is-app .screen--push:not(.screen--tab) {
    /* Lådan börjar under topplisten och går över menyn. Listen är
       fönstrets egen rad och ska alltid synas; menyn är ett val man redan
       gjort när lådan är öppen. */
    top:var(--topbar);
    left:auto; right:0; width:var(--drawer);
    /* Över menyn och över skuggan. Höjdordningen på skrivbordet är:
       flikvy 10, hemsida 20, meny 40, skugga 45, låda 50. */
    z-index:50;
  }
  /* Och ett snäpp över den medan den glider ut. Samma skäl som på
     telefonen: annars glider lådan ut bakom den man kommer till och
     rörelsen syns aldrig. Se glidUt i routern. */
  body.is-app .screen--push:not(.screen--tab).is-leaving {
    z-index:51;
    /* Kanten lättare än --line. Linjen ska säga var lådan börjar, inte
       rita en ram: mot en yta i nästan samma färg läste kantfärgen som en
       kontur runt ett kort. */
    border-left:1px solid rgba(var(--ink-rgb),.12); border-right:0;
    /* Ingen skugga. Den skulle lyfta lådan från ytan bakom, men bakom den
       ligger antingen täckaren — som redan mörkar — eller hemsidan, och
       där blev den ett grått fält längs kanten som inte hörde till någon
       av dem. Kanten räcker för att säga att lådan ligger ovanpå. */
    box-shadow:none;
  }

  /* Skuggan bakom lådan. Ligger under hemsidan i höjd, så den syns bara
     när det som öppnats inte har någon hemsida att visa: inställningar,
     konto, ett svar i inkorgen. */
  body.is-app .deskScrim {
    display:block; position:fixed; inset:0; z-index:45;
    background:rgba(10,10,10,.28);
    opacity:0; pointer-events:none;
    transition:opacity .28s cubic-bezier(.25,.46,.45,.94);
  }
  /* Bara när det som öppnats saknar hemsida att visa. Sidor och sektioner
     har lagret i stället, och det ska inte ligga i skugga.

     Ytan tar emot klick när den syns. Att trycka utanför en låda är hur
     man stänger en låda; pilen i listen är för den som letar efter en
     knapp. */
  body.is-app #app:has(.screen--push:not(.screen--tab):not([data-preview]).is-active) .deskScrim {
    opacity:1; pointer-events:auto;
  }

  /* Inställningarna och allt som öppnas därifrån — medlemmar, minne,
     domän, profil, AI-chatten — är ett lager över hela fönstret, listen
     inräknad. De gäller kontot och projektet, inte fliken man står i,
     och ska kunna öppnas varifrån som helst utan att något under dem
     byts. Skuggan lägger sig då även över topplisten: allt bakom lagret
     är bakgrund, listen också. */
  body.is-app .screen--push.screen--global { top:0; z-index:56 }
  body.is-app #app:has(.screen--global.is-active) .deskScrim { z-index:55 }

  /* ---- topplisten ----
     Från menyns kant till fönstrets, som ett fönsters egen list. Den bär
     det som gäller en själv och inte arbetet: vilket projekt man är inne
     i, klockan och kontot. Arbetet står i menyn till vänster om den.

     Samma botten som menyn och innehållet, och en linje i underkant i
     stället för en egen fyllning. Listen låg förr i bläcket och menyn i
     kortfärgen: tre svarta i samma bild, ingen av dem lika, och skalet
     lästes som tre lager i stället för ett fönster. Linjen säger var
     listen slutar; en egen färg påstod dessutom att den var något annat. */
  body.is-app .topbar {
    /* Ett snäpp över skärmtäckaren.

       Båda låg på 45, och täckaren står senare i märkspråket — alltså
       vann den. Med en låda öppen låg den ovanpå listen, och ett tryck på
       kugghjulet, klockan eller kontot träffade täckaren i stället: lådan
       stängdes, knappen gjorde ingenting, och man fick trycka en gång
       till. Två testare rapporterade det som att appen ibland struntade i
       första klicket.

       Den modala lådan (screen--global) höjer täckaren till 55 och ligger
       kvar över listen — där ÄR det meningen att inget annat ska nås. */
    position:fixed; top:0; left:var(--nav); right:0; z-index:46;
    /* flex-end kvar: märket bär margin-right:auto och skjuter resten åt
       höger av sig självt. Göms märket — på projektlistan, där inget
       projekt är öppet — ska knapparna ligga kvar till höger. */
    /* Lodrätt centrerat. Listen var toppställd när menyn stod i den och
       de tre skulle dela överkant; nu bär den bara hörnets knappar, och
       de står mitt i sin rad. */
    display:flex; align-items:center; justify-content:flex-end; gap:1rem;
    height:var(--topbar); padding:0 1.25rem;
    background:var(--background); color:var(--foreground);
    /* Linjen som skiljer skalet från arbetet. Den går tvärs över hela
       fönstret: menyn ritar sin halva själv, se .tabbar::after. Förr låg
       skiljelinjen lodrätt längs menyns kant i stället — en kant som
       delade fönstret i två spalter hela vägen ned. En vågrät linje
       säger samma sak om mindre: här slutar ramen, under den är det du
       håller på med. */
    border-bottom:1px solid rgba(var(--ink-rgb),.08);
    transition:transform .25s cubic-bezier(.25,.46,.45,.94);
  }
  /* Ikonerna står tätt, kontot en aning ifrån.

     Tecknen är 1,125 rem breda i rutor på 2 rem, så rutornas egen
     stoppning bär redan .875 rem mellan dem — la man en gap ovanpå det
     blev avståndet lika stort som tecknet självt, och klockan och kuggen
     lästes som två skilda saker i stället för ett par. Rutorna får därför
     ligga kant i kant, och luften läggs där gränsen faktiskt går: före
     kontot, som är något annat än de två. */
  /* Inget vänsterhörn i listen längre. Projektväljaren står överst i
     menyn bredvid (.tabbar__proj) och märket nederst i den. */
  /* Adressen får svälla till en gräns och sedan inte mer.

     En förhandsadress är av formen projekt-abc123.web.app och en kopplad
     domän kan vara nästan hur lång som helst; utan tak sköt knappen menyn
     ur mitten på de projekt som råkade ha ett långt namn. 20 rem rymmer
     de allra flesta adresser hela, och de som inte får plats slutar med
     tre punkter — hela adressen står i titeln. */
  /* Projektväljaren stod här bredvid klockan; den står överst i menyn
     nu, se .tabbar__proj. */
  /* Ingen växel i pillret längre. Den stod efter adressen som en pil i
     slutet av en rad, men pillret säger redan självt att det går att
     trycka på — se index.php. */

  body.is-app .topbar__end { display:flex; align-items:center; gap:0 }
  /* Två knappar före ikonparet: uppmaningen att koppla domän, och vägen
     ut till sajten. Lägre än en knapp i ett formulär — listen är 3,5 rem
     och en knapp på 2,5 rem hade fyllt den nästan helt — och med luft
     till klockan så att paret bredvid fortsatt läses som ett par.

     .375 rem till klockan: rutan bär .4375 rem egen stoppning, alltså
     .8125 rem från knappens kant till klockans bläck. */
  /* Samma höjd som projektväljaren i motsatt hörn, inte som menyn.

     Den var 2,75 rem för att möta menyns över- och underkant, och blev då
     den tyngsta saken i listen — en knapp som vägde mer än navigeringen.
     Nu är de tre i stället lodrätt centrerade på samma linje: väljaren och
     knappen på 2,25 rem, menyn på 2,75. Olika höjd, samma mittlinje, och
     det är mittlinjen ögat läser. */
  /* Nätt. Den var 2,25 rem hög med ordinarie knapptext och vägde mer
     än allt annat i listen — en platta bredvid två tunna ikoner. Nu är
     den 1,75 rem, strax över tecknens 1,125, med mindre text: en
     uppmaning i hörnet, inte en knapp i ett formulär. */
  body.is-app .topbar__cta {
    min-height:1.75rem; padding:.125rem .75rem; font-size:.75rem;
    font-weight:500; letter-spacing:.005em;
    white-space:nowrap; margin-right:.625rem;
  }
  body.is-app .topbar__cta:hover { opacity:.85 }
  body.is-app .topbar__cta[hidden] { display:none }
  /* Vägen ut till sajten, bredvid uppmaningen. Samma mått som den — de
     står i samma rad och ska väga lika mycket — men i kontur: den ena är
     något man gör en gång, den andra något man gör hela dagen.

     Marginalen är uppmaningens, så att luften till klockan blir densamma
     vilken av de två som råkar stå sist. Uppmaningen försvinner när
     domänen finns. */
  body.is-app .topbar__site {
    min-height:1.75rem; padding:.125rem .75rem; font-size:.75rem;
    font-weight:500; letter-spacing:.005em;
    white-space:nowrap; margin-right:.625rem;
  }
  /* Runda, som kontots bricka bredvid. Rutorna är 2 rem kring tecken på
     1,125 rem, alltså .4375 rem stoppning: bläcket i klockan står då
     .875 rem från bläcket i kuggen. Kontot får .1875 rem marginal så
     avståndet dit blir detsamma — brickan bär .25 rem egen stoppning,
     och utan marginalen stod den .1875 rem närmare än paret gör. */
  body.is-app .topbar__btn {
    position:relative; display:grid; place-items:center;
    width:2.25rem; height:2.25rem; border-radius:50%;
    color:rgba(var(--ink-rgb),.7);
  }
  /* Samma kant vid pekaren som projektväljaren i menyn (.tabbar__proj):
     två knappar i samma list ska svara likadant på samma rörelse. */
  body.is-app .topbar__btn:hover {
    background:rgba(var(--ink-rgb),.06); color:var(--foreground);
    box-shadow:inset 0 0 0 1px rgba(var(--ink-rgb),.35) }
  body.is-app .topbar__btn svg {
    width:1.25rem; height:1.25rem; stroke:currentColor; fill:none;
    stroke-width:1.75; stroke-linecap:round; stroke-linejoin:round;
  }
  /* Siffran sitter i ikonens hörn och bär en ring i listens egen färg, så
     den läses som en markering på klockan och inte som ett löst element. */
  body.is-app .topbar__btn .tab__count {
    top:.25rem; left:auto; right:.25rem; margin:0; transform:none;
    border-color:var(--background);
  }
  /* Kontot är numera en tredje ikonknapp och inte en bricka med
     initialer. Reglerna för brickan är därför borta — och med dem
     marginalen som fanns för att brickan bar egen stoppning. */

  /* ---- menyn: en spalt längs vänsterkanten ----

     Den var en flytande rad mitt i listen ett tag: samma fem ställen,
     ingen spalt, arbetet fick hela bredden. Men på en bred skärm stod
     arbetet ändå i en spalt på 54 rem mitt i fönstret, och pillret
     ovanför den hängde i luften mellan ett märke i ena hörnet och tre
     knappar i det andra — tre grupper på en rad utan något som höll ihop
     dem. Bredden fanns; det var ordningen som saknades.

     Spalten ger listen en plats att börja på: märket överst, de fem
     ställena under varandra där ögat läser en lista, och utgången
     nederst där man letar efter den i varje annan meny. Fjorton rem är
     inte längre något man ger bort — bredvid en spalt på 54 rem i ett
     fönster på 110 finns de.

     Positionen är fixed: menyn hör till ramen och inte till någon vy,
     och sidan bredvid rullar. */
  body.is-app .tabbar {
    top:0; bottom:0; left:0; right:auto;
    width:var(--nav); height:auto; z-index:47;
    transform:none;
    flex-direction:column; justify-content:flex-start; align-items:stretch;
    /* Överkanten mäter mot listen: väljaren är 2,25 rem och listen 3,5,
       så .625 rem över ger dem samma mittlinje. */
    gap:.125rem; padding:.625rem .75rem 1.25rem;
    border-radius:0; border:0;
    /* Ingen lodrät kant längre.

       Den gick längs hela skärmhöjden och delade fönstret i två spalter.
       Nu är skiljelinjen vågrät och ligger under topplisten i stället —
       en linje som säger "här slutar ramen" i stället för två som säger
       "här är två spalter". Menyn och innehållet delar botten och flyter
       ihop under linjen, och det är meningen. */
    /* Samma botten som listen och innehållet — se topplistens kommentar
       om tre svarta i samma bild. */
    background:var(--background);
    -webkit-backdrop-filter:none; backdrop-filter:none;
    box-shadow:none;
  }
  body.is-app.has-project .tabbar { display:flex; transform:none }

  /* Topplistens linje, fortsatt genom menyspalten.

     Listen börjar vid menyns kant (left:var(--nav)), så dess egen
     border-bottom täcker bara högra delen av fönstret. Den här ritar
     resten, på exakt samma höjd, så att linjen går obruten från kant
     till kant.

     Absolut placerad mot .tabbar, som är fixed och därmed är sitt eget
     innehållsblock: top mäts från menyns överkant och left/right från
     dess ytterkanter — menyns egen stoppning ligger innanför och flyttar
     inte linjen. */
  body.is-app .tabbar::after {
    content:''; position:absolute; left:0; right:0;
    /* Minus en pixel. Listens egen linje är en border-bottom, och med
       box-sizing:border-box ligger den INNANFÖR dess höjd — alltså på
       raden före var(--topbar). Utan avdraget mötte halvorna varandra
       med ett pixelsteg mitt i fönstret. Mätt, inte gissat. */
    top:calc(var(--topbar) - 1px);
    height:1px; background:rgba(var(--ink-rgb),.08); pointer-events:none;
  }

  /* Projektväljaren överst i spalten. Så bred som adressen, inte som
     spalten: ett piller över hela bredden lästes som en rubrikrad, och
     en väljare ska se ut som en väljare. Ingen kant utan pekare — pilen
     nedåt efter adressen säger redan att det är en rullgardin, och
     kanten kommer när handen är på väg, som listens övriga knappar. */
  body.is-app .tabbar__proj {
    display:flex; width:fit-content; max-width:100%; justify-content:flex-start;
    gap:.375rem;
    min-height:2.25rem; padding:.3125rem .625rem .3125rem .875rem; font-size:.875rem;
    white-space:nowrap; margin-bottom:1rem;
    box-shadow:none;
  }
  body.is-app .tabbar__proj[hidden] { display:none }
  body.is-app .tabbar__addr {
    min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  /* Pilen: samma streck som listens ikoner, vriden nedåt. Dämpad — det
     är adressen man läser, pilen är bara upplysningen om att den går
     att byta. */
  body.is-app .tabbar__chev {
    flex:0 0 auto; width:.875rem; height:.875rem;
    transform:rotate(90deg);
    color:rgba(var(--ink-rgb),.55);
    stroke:currentColor; fill:none; stroke-width:2;
    stroke-linecap:round; stroke-linejoin:round;
  }
  body.is-app .tabbar__proj:hover {
    background:rgba(var(--ink-rgb),.06);
    box-shadow:inset 0 0 0 1px rgba(var(--ink-rgb),.35);
  }
  body.is-app .tabbar__proj:hover .tabbar__chev { color:var(--foreground) }

  body.is-app .tabbar__mid { display:flex; flex-direction:column; gap:.125rem }

  /* Märket nederst, som avsändare. Fullt vitt och en rem högt: dämpat
     och litet lästes det som en vattenstämpel, och en avsändare ska gå
     att läsa. Inverteras på ljus botten, se --logoOnBar. */
  /* Ingen linje ovanför: märket är inte en grupp i menyn utan
     avsändaren under den, och en linje gjorde det till en rad till. */
  body.is-app .tabbar__end {
    display:flex; align-items:center; margin-top:auto;
    padding:0 .875rem;
  }
  body.is-app .tabbar__logo {
    height:1rem; width:auto; display:block; opacity:1;
    filter:var(--logoOnBar, none);
  }

  /* Flikarna som rader. Ikon och ord bredvid varandra: ordet ensamt
     räcker inte när raden ska gå att träffa i förbifarten, och ikonen
     ensam kräver att man lärt sig fem symboler först. */
  body.is-app .tab {
    flex:0 0 auto; max-width:none; width:100%;
    flex-direction:row; align-items:center; justify-content:flex-start;
    gap:.75rem; height:2.75rem; padding:0 .875rem;
    border-radius:.75rem;
    color:rgba(var(--ink-rgb),.62);
    text-align:left;
  }
  body.is-app .tab svg { width:1.1875rem; height:1.1875rem; flex:0 0 auto }
  body.is-app .tab span { font-size:.9375rem; letter-spacing:-.005em }
  /* Ordet tillbaka. Det göms bara i telefonens rad, där fem ord under
     fem ikoner tar höjd från det man siktar på — här står de bredvid
     ikonen i en rad som är gjord för dem. */
  body.is-app .tab__l {
    position:static; width:auto; height:auto; margin:0;
    overflow:visible; clip-path:none;
  }
  body.is-app .tab.is-active {
    background:rgba(var(--ink-rgb),.08); color:var(--foreground); font-weight:600 }
  body.is-app .tab:hover { color:var(--foreground) }
  body.is-app .tab:hover:not(.is-active) { background:rgba(var(--ink-rgb),.045) }

  /* Siffran i ikonens hörn. Raden har en högerkant, men den ligger långt
     från ordet på en bred spalt, och en siffra där hade lästs som något
     annat än flikens. Ikonen börjar .875 rem in och är 1,1875 bred; .5 rem
     in från dess högerkant ger brickan hörnet att sitta i. Ringen i
     bakgrundsfärgen klipper ut den ur ikonen. */
  body.is-app .tab span.tab__count {
    top:.375rem; left:1.5625rem; right:auto; margin:0; transform:none;
    min-width:.9375rem; height:.9375rem;
    font-size:.5625rem; font-weight:700;
    border:2px solid var(--background);
  }
  body.is-app .tab span.tab__count--dot { min-width:.4375rem; height:.4375rem;
    top:.625rem; left:1.875rem; border-width:0 }

  /* flex-start som på telefonen: vänsterblocket är rubrik plus domän,
     och Visa hemsida ska stå i linje med rubrikens rad längst till
     höger. Radhöjden 2,75 rem bär .bar__homeWord globalt, så Hem står
     på samma punkt som de andra flikarnas rubriker. */
  body.is-app #screen-dashboard .bar {
    position:static;
    display:flex; align-items:flex-start; justify-content:space-between;
    height:auto;
    /* Samma stoppning som .screen--tab .screenHead, alltid. Assistent
       ritas med en .bar och inte med ett screenHead, och de två måtten
       måste därför ändras i par — rubrikerna ska ligga på samma linje
       oavsett vilken byggsten som råkar rita dem.

       Siffran har varit fel förut åt båda håll: först 2 rem mot huvudets
       4,5, sedan 0,25 mot huvudets 1,5. Står de isär hoppar rubriken när
       man byter flik. Uppmätt när de är i par: ordmärket och rubrikerna
       börjar alla på 80 px. */
    padding:1.5rem 1.25rem 0;
    background:transparent;
  }

  /* Assistenten smalare än de andra flikarna: en läsbredd plus
     innehållets stoppning på var sida. Listorna behåller den bredare
     spalten — en tabell vill ha bredden, ett samtal vill ha en läsbar
     rad. */
  body.is-app #screen-dashboard { --spalt:calc(var(--meny) + 2.5rem) }

  /* Ingen bakåtpil i sidans egen låda, och rubriken vid kanten.

     Lådan öppnas ur listan och stängs med den stora pilen uppe till
     vänster, som tar en ut ur sidan. En pil till inne i lådan pekade på
     samma väg en gång till och såg ut att vara ett steg tillbaka i något —
     två knappar för en sak, och den ena på fel ställe.

     Rutan pilen satt i tas bort ur rutnätet i stället för att lämnas tom:
     en centrerad rubrik med ett hål till vänster om sig ser centrerad ut
     av misstag. Nu börjar rubriken där texten under den börjar.

     Sektionens låda behåller sin pil: där ÄR den ett steg tillbaka. */
  body.is-app #screen-page > .bar { grid-template-columns:1fr 2.75rem }
  body.is-app #screen-page > .bar > [data-back] { display:none }
  body.is-app #screen-page > .bar > .bar__title { text-align:left }

  /* Klockan och knappen ut till sajten i Hems rad hör till telefonen. På
     skrivbordet står de i topplisten, där de gäller varje vy och inte
     bara den här — och två Visa webbplats i samma hörn är en för mycket.

     Kuggen står kvar. Den är flikens egen och inte appens — topplisten
     har ingen längre — och de andra flikarna har sin på samma plats:
     längst till höger i rubrikraden. Samma piller som deras, se .wGear. */
  body.is-app #screen-dashboard .bar [data-notices],
  body.is-app #screen-dashboard .bar .homeSite { display:none }
  body.is-app #screen-dashboard .bar [data-settings] {
    width:2.5rem; height:2.5rem; margin:.125rem 0 0;
    background:transparent; color:var(--foreground);
    box-shadow:inset 0 0 0 1px rgba(var(--ink-rgb),.35);
    border-radius:var(--radius-pill);
  }
  body.is-app #screen-dashboard .bar [data-settings]::before { content:none }
  body.is-app #screen-dashboard .bar [data-settings]:hover {
    background:rgba(var(--ink-rgb),.045);
    box-shadow:inset 0 0 0 1px rgba(var(--ink-rgb),.6);
  }
  body.is-app #screen-dashboard .bar [data-settings] svg { width:1.125rem; height:1.125rem }

  /* Och rubriken bär namnet igen.

     Härifrån och uppåt finns topplisten med sin egen växel i vänsterhörnet,
     och två växlare i samma hörn är en för mycket. Namnet är dessutom den
     bättre rubriken när det får plats: det är vad man kallar projektet,
     medan adressen är vad man känner igen det på i en lista. */
  body.is-app #screen-dashboard .bar__homeName { display:block }
  body.is-app #screen-dashboard .homeProj { display:none }

  /* Rubrikraden får samma luft ned till adressraden som de andra
     flikarnas rubriker har till sitt innehåll. */
  body.is-app #screen-dashboard .bar { padding-bottom:.875rem }

  /* Att skriva ett inlägg är en knapp för tummen. Vid ett tangentbord
     finns Skriv nytt inlägg där inläggen står, och en plusknapp mitt i
     menyn vore bara en till väg till samma ställe. */

  /* Panelen hänger under klockan, i listens högerkant. Inte i mitten:
     den hör till knappen man tryckte på, och en ruta som dyker upp
     någon annanstans än där man tittade måste letas rätt på. */
  body.is-app .noticePop {
    top:calc(var(--topbar) + .5rem); right:1.25rem; left:auto;
    width:24rem;
    max-height:min(28rem, calc(100dvh - var(--topbar) - 3rem));
  }

  /* Kvittot och notiserna hör till spalten, inte till fönstret. */
  body.is-app .saveNote { left:calc(var(--nav) + (100% - var(--nav)) / 2); bottom:1rem }
  body.is-app #toasts, body.is-app.has-tabs #toasts {
    left:calc(var(--nav) + (100% - var(--nav)) / 2); bottom:1rem; max-width:22rem;
  }

  /* Dialogen är en bottenlåda på telefon. Här får den bli en dialog, mitt
     på skärmen där blicken redan är. */
  body.is-app .sheet__panel {
    left:calc(var(--nav) + (100% - var(--nav)) / 2);
    right:auto; bottom:auto; top:50%;
    width:min(24rem, calc(100vw - 3rem));
    border-radius:1.25rem;
    padding:1.5rem 1.25rem;
    transform:translate3d(-50%,-46%,0); opacity:0;
    transition:transform .22s cubic-bezier(.25,.46,.45,.94), opacity .22s;
  }
  body.is-app .sheet__scrim { position:fixed; inset:0 }
  /* Som dialog behöver den kanten runt om, inte bara upptill: den svävar
     mitt på skärmen med appen på alla fyra sidor. */
  body.is-app .sheet__panel {
    box-shadow:inset 0 0 0 1px rgba(var(--ink-rgb),.14),
               0 1.5rem 3.5rem -1rem rgba(0,0,0,.5);
  }
  body.is-app .sheet.is-in .sheet__panel { transform:translate3d(-50%,-50%,0); opacity:1 }
  body.is-app .sheet__grip { display:none }

  /* Kortväljaren får bredden den behöver.

     Sektionstyperna är sjutton kort, och i två spalter på 24 rem blev det
     nio rader att rulla igenom för ett val som ska gå att se på en gång.
     Vid ett tangentbord finns bredden — då ska den användas. Panelen mäts
     på sitt innehåll (:has) i stället för på en klass, för väljaren är
     samma dialog som allt annat och ska inte behöva veta det. */
  body.is-app .sheet__panel:has(.pickcards) {
    width:min(46rem, calc(100vw - 6rem));
  }
  body.is-app .pickcards {
    grid-template-columns:repeat(3, 1fr);
    max-height:min(46rem, 74dvh);
    padding-right:.5rem;
  }
  @media (min-width:1500px) {
    body.is-app .sheet__panel:has(.pickcards) { width:min(58rem, calc(100vw - 6rem)) }
    body.is-app .pickcards { grid-template-columns:repeat(4, 1fr) }
  }

  /* Andra flikens ord vid ett tangentbord: hemsidan står redan bredvid,
     fliken är samtalet och heter Assistent. Ordningen — Innehåll först —
     är numera densamma överallt och ligger i märkspråket, se pageTabs(). */
  body.is-app .seg--ptab .ptabWord--desk { display:inline }
  body.is-app .seg--ptab .ptabWord--phone { display:none }

  /* Skrivfältet lyfts från underkanten.

     I telefonen ligger raden mot skärmens nederkant, där tummen är. Vid
     ett tangentbord är den nedre kanten längst bort från både blicken och
     händerna — och en tom svart yta under fältet gjorde att man siktade
     ned i hörnet varje gång. Fyra rem luft räcker för att raden ska höra
     till samtalet i stället för till fönsterkanten. */
  /* Mindre än förut: märket under skrivfältet bär numera en del av
     avståndet ned till fönsterkanten. */
  body.is-app #dashboard-body .llm { padding-bottom:2rem }

  /* Raden får mer att stå i vid ett tangentbord.

     På telefon är den smal av nödvändighet — skärmen är kort och
     tangentbordet tar halva. På en stor skärm läses samma mått som något
     hoptryckt: en tunn remsa i en vid yta. Stoppningen växer, fältet
     likaså, och raden blir det den är på den här skärmen — samtalets
     ingång och inte en list längst ned. */
  body.is-app #dashboard-body .llm__bar {
    padding:.75rem;
    border-radius:1.75rem;
  }

  /* Fältets stoppning räknas, den gissas inte.

     Raden är align-items:flex-end — knapparna ska stanna vid nedersta
     raden när texten växer, som i en meddelandeapp. Priset är att allt som
     inte är lika högt hamnar snett: fältet stod på 45 px mot knapparnas
     36, och de tre knapparna satt därför 4,5 px under textens mitt. Med
     raden bredare på skrivbordet blev glappet stort nog att synas.

     En rad text är 15 px × 1,4 = 21 px. Knappen är 2,25 rem = 36 px.
     (36 − 21) / 2 = 7,5 px = .46875rem, och då delar fältet och knapparna
     mittlinje utan att flex-end behöver ge upp sitt uppdrag. Höjden raden
     förlorar tas igen av rutans egen stoppning ovan — den hör hemma där,
     för det är rutan som ska vara rymlig och inte fältet som ska vara
     feltäljt.

     Stoppningen är symmetrisk i sidled av samma skäl: plus till vänster
     och skicka till höger är lika stora tryckytor, och de ska börja lika
     långt från varsin kant. */
  body.is-app #dashboard-body textarea.llm__input {
    padding:.46875rem .5rem;
    font-size:.9375rem;
  }

  /* Samtalet ska fylla lådan och fältet ligga i botten, inte flyta mitt i. */
  body.is-app #page-body.body--fill { padding-bottom:1rem }

  /* Minnet står i den vanliga spalten.

     Globen fyllde förut hela fönstret, för då var den en egen skärm. Nu är
     den en tredjedel av höjden med en lista under sig, och en lista i full
     fönsterbredd är rader som sträcker sig en meter från sin egen rubrik.
     Spalten är rätt mått för det som ska läsas; globen får den bredd den
     får och blir inte sämre av det. */

  /* ---- hemsidan som lager ----
     Ligger över flikvyn men under lådan, och finns bara när det som
     öppnats faktiskt är en del av webbplatsen. */
  body.is-app .deskPrev {
    /* Ett enda mått för all luft runt och mellan vyerna: till vänster om
       skrivbordsvyn, mellan den och telefonen, och mellan telefonen och
       lådan. Tre olika tal där lästes som slarv, för ögat mäter avstånden
       mot varandra och inte mot någon regel i en fil. */
    --prevgap:5.25rem;

    display:flex; flex-direction:column;
    position:fixed; inset:0; z-index:20;
    padding:var(--prevgap);
    padding-top:calc(var(--topbar) + var(--prevgap));
    padding-left:calc(var(--nav) + var(--prevgap));
    padding-right:calc(var(--drawer) + var(--prevgap));
    background:var(--surface-2);
    /* Tonas fram i stället för att slås på och av. Lådan glider ut på en
       fjärdedels sekund, och ett lager som försvann i samma stund den
       började röra sig lämnade ett blänk av vyn bakom. Ligger kvar i
       layouten hela tiden, så hemsidan är färdigritad när den ska fram. */
    opacity:0; visibility:hidden;
    transition:opacity .22s ease, visibility .22s;
  }
  body.is-app #app:has(.screen--push[data-preview].is-active) .deskPrev {
    opacity:1; visibility:visible;
    /* Helskärm. Att redigera en sida är ett läge man går in i, och då ska
       hemsidan få hela fönstret — listen hör till att välja vad man ska
       göra, inte till att göra det. Luften är densamma runt om, utom
       upptill: där står bakåtpilen på 1 rem, och växeln ska stå på samma
       rad som den. Raden är 2,75 rem hög som pilen, och hemsidan börjar
       en rem under den. Förut låg växeln en hel prevgap ned och pilen i
       hörnet ovanför — två saker på två rader där en rad räcker. */
    padding-top:1rem;
    padding-left:var(--prevgap);
  }

  /* Allt skal glider undan under tiden: menyn åt vänster, topplisten
     uppåt. Att redigera en sida är ett läge man går in i, och då ska
     hemsidan och lådan ha hela fönstret — en mörk rad över dem hade sagt
     att man fortfarande står utanför och tittar in. Vägen tillbaka är
     pilen i hörnet. */
  /* Menyn åker ut åt vänster: den är en spalt längs vänsterkanten igen,
     och en spaltbredd åt vänster tar den ur bild. (Den var ett piller
     mitt i listen ett tag och lyftes då uppåt i stället.) */
  body.is-app #app:has(.screen--push[data-preview].is-active) .tabbar {
    transform:translate3d(-100%,0,0);
    pointer-events:none;
  }
  body.is-app #app:has(.screen--push[data-preview].is-active) .topbar {
    transform:translate3d(0,-100%,0);
  }
  /* Lådan följer med upp när listen gått undan: annars ligger en tom
     remsa kvar i toppen där den stod. */
  body.is-app #app:has(.screen--push[data-preview].is-active) .screen--push:not(.screen--tab) {
    top:0;
  }

  body.is-app #app:has(.screen--push[data-preview].is-active) .deskBack {
    display:inline-flex;
  }
  body.is-app .deskBack {
    align-items:center; justify-content:center;
    position:fixed; top:1rem; left:1.25rem; z-index:25;
    width:2.75rem; height:2.75rem;
    border-radius:var(--radius-pill);
    background:var(--card); color:var(--foreground);
    box-shadow:0 .25rem 1rem rgba(10,10,10,.14);
  }
  body.is-app .deskBack svg {
    width:1.5rem; height:1.5rem; stroke:currentColor; fill:none;
    stroke-width:2; stroke-linecap:round; stroke-linejoin:round;
  }
  body.is-app .deskBack:active { transform:scale(.92) }

  /* ---- förhandsgranskningen ----
     Hela fönstret, inte en låda i kanten.

     Den låg som alla andra lådor: en spalt till höger, under topplisten,
     med hemsidan hoptryckt i den. Men det man gör här är inte att fylla i
     något vid sidan av sajten — det ÄR sajten man tittar på, och en sida
     som ska bedömas ska ses i den bredd besökaren ser den i. En sajt
     visad i en tredjedels fönster går inte att bedöma, den går bara att
     ana.

     Samma läge som redigeringens: skalet glider undan och kvar är sidan
     och en väg tillbaka i det hörn blicken söker. Skillnaden är att här
     behövs ingen meny alls — man tittar, man ändrar inget. */
  body.is-app #screen-preview {
    top:0; left:0; right:0; width:auto;
    /* Över topplisten (46), skärmtäckaren (55) och de globala lådorna
       (56). Ingenting ska ligga över sidan man granskar. */
    z-index:60;
    border-left:0;
  }
  /* Listen bär bara ordet Förhandsgranskning och en pil, och båda säger
     saker man redan vet: att man tittar, och hur man slutar. Pilen bor i
     hörnet i stället, som en knapp ovanpå sidan. */
  body.is-app #screen-preview > .bar { display:none }
  body.is-app #screen-preview .previewBack { display:inline-flex; z-index:62 }
  /* Växeln flyter ovanpå sidan i stället för att ta en egen rad över den.
     Raden var en grå remsa i toppen av en helskärmsvy, och det första man
     såg av sin sajt var då något som inte hörde till den. */
  /* Nedtill och inte upptill.

     Pillret svävade över sidans överkant, alltså rakt ovanpå sajtens egen
     meny. Där låg det i vägen för det man kom för att titta på: att prova
     en menylänk i förhandsgranskningen betydde att först sikta förbi en
     knapp som hörde till appen och inte till sidan.

     Nederkanten är fri på nästan varje sajt — sidfoten är det man rullar
     till, inte det man klickar i — och pillret hamnar dessutom närmare
     handen på en bärbar skärm. */
  body.is-app #screen-preview .previewSeg {
    position:absolute; bottom:1.25rem; left:50%; transform:translateX(-50%);
    z-index:62; margin:0;
    box-shadow:0 .25rem 1rem rgba(10,10,10,.14);
  }
  /* Telefonen står mitt på ytan och ska inte ligga an mot fönsterkanten.
     Luften ligger nedtill nu, där pillret flyter — annars täcker det
     sidfoten i telefonvyn. */
  body.is-app.prev-mobile #screen-preview .previewFull {
    margin-top:0; margin-bottom:4.25rem;
  }

  /* Krysset som stängde lådan låg här. Det är borta: lådans egen
     rubrikrad har redan en bakåtpil en centimeter därifrån, och två
     knappar för samma sak i samma huvud är en för mycket. Kvar är pilen,
     ytan utanför och Escape. */

  /* Raden bakåtpilen ligger i, med växeln mitt i. Samma höjd som pilen
     (2,75 rem) så de delar mittlinje, och en rem ned till hemsidan. Tom
     när växeln inte behövs, men inte onödig: utan den skulle pilen hamna
     ovanpå sajtens egen list. */
  .deskPrev__bar {
    flex:0 0 auto; height:2.75rem; margin-bottom:1rem;
    display:flex; align-items:center; justify-content:center;
  }
  /* Växeln syns bara när bara en av dem får plats. Se mediefrågan nedan. */
  .deskPrev__toggle { width:auto; padding:.1875rem }
  .deskPrev__toggle button { padding:0 .875rem; height:1.875rem; font-size:.75rem }

  .deskPrev__row {
    flex:1 1 auto; min-height:0;
    display:flex; gap:var(--prevgap); align-items:stretch; justify-content:center;
  }

  .deskPrev__stage {
    flex:1 1 auto; position:relative; overflow:hidden;
    border-radius:1rem; background:#fff;
    box-shadow:0 0 0 1px var(--line), 0 1.25rem 3rem rgba(10,10,10,.10);
  }
  .deskPrev__frame {
    position:absolute; top:0; left:0;
    width:1180px; height:100%;
    border:0; transform-origin:0 0;
  }

  /* Telefonen bredvid. Egen ritning i egen bredd, inte en nedkrympt
     kopia: sajten är responsiv, och det man behöver se är skillnaden. */
  /* Telefonen är en telefon: en iPhone 12:s format, mitt i ytan. Måtten
     sätts av fitPhonePreview — rutan behåller 390 gånger 844 och krymps
     med skala när höjden inte räcker, i stället för att dras ut till en
     bred remsa. */
  .deskPrev__phone {
    flex:0 0 auto; position:relative; overflow:hidden;
    align-self:center;
    width:24.375rem; height:52.75rem;
    border-radius:1.75rem; background:#fff;
    box-shadow:0 0 0 1px var(--line), 0 1.25rem 3rem rgba(10,10,10,.10);
    display:none;
  }
  .deskPrev__mframe {
    position:absolute; top:0; left:0;
    width:390px; height:844px; border:0; transform-origin:0 0;
  }

  /* Vad som visas beror på bredden.

     Får båda plats står de bredvid varandra och växeln behövs inte. Får
     de inte det visas en, och växeln säger vilken. Gränsen är satt där
     skrivbordsvyn annars hade krympt till en miniatyr: spalten, lådan och
     telefonen plus luften mellan dem. */
  body.is-app .deskPrev__toggle { display:none }

  /* Ensam står telefonen mitt i ytan och behåller sin bredd. En
     telefonvy utdragen till tusen pixlar är ingen telefonvy. */
  body.is-app.prev-mobile .deskPrev__stage { display:none }
  body.is-app.prev-mobile .deskPrev__phone { display:block; margin:0 auto }

/* Gränsen där båda ryms bredvid varandra.

     Låg på 1700 och var för lågt satt. Med lådan på 28 rem, spalten och
     luften mellan dem blev skrivbordsvyn där en remsa på knappt tusen
     pixlar — bred nog att kallas en skrivbordsvy, för smal för att visa
     hur sajten faktiskt ser ut på ett skrivbord. Man tittade på något som
     inte var någondera.

     2300 är där skrivbordsvyn får sin egna riktiga bredd med telefonen
     bredvid. Under det visas en i taget och växeln säger vilken — hellre
     en vy som stämmer än två som inte gör det. */
  @media (min-width:2300px) {
    body.is-app .deskPrev__phone { display:block }
    /* Med båda framme betyder växeln ingenting. */
    body.is-app.prev-mobile .deskPrev__stage { display:block }
    body.is-app.prev-mobile .deskPrev__phone { margin:0 }
  }

  @media (max-width:2299px) {
    body.is-app .deskPrev__toggle { display:flex }
  }

  /* På en bärbar skärm är 30 rem för mycket att ge bort. Lådan krymper
     hellre än att hemsidan gör det: den ska gå att läsa. */
  @media (max-width:1499px) {
    body.is-app #app { --spalt:48.5rem; --drawer:25rem }
    /* Trängre skärm, mindre luft — men fortfarande lika mycket överallt. */
    body.is-app .deskPrev { --prevgap:3rem }
  }
}

/* ============================ dragspel ============================
   En rad per domän. Rubriken är hela tryckytan, och tillståndet syns utan
   att man behöver fälla ut. */
.acc { border-top:1px solid var(--line) }
.acc__item { border-bottom:1px solid var(--line) }
.acc__head { display:flex; align-items:center; gap:.75rem; width:100%; text-align:left;
  padding:1rem .25rem }
.acc__text { flex:1 1 auto; min-width:0 }

/* Två knappar på en historikrad: Lägg ut igen och Återställ. De ska inte
   sträckas ut av raden, och på en telefon ska de få bryta under varandra
   hellre än att klämma ihop texten till vänster. Se releaseLog. */
.rowBtns { display:flex; flex-wrap:wrap; justify-content:flex-end;
  gap:.375rem; flex:0 0 auto }
.rowBtns .btn { width:auto; flex:0 0 auto }
.acc__title { display:block; font-size:1rem; font-weight:600; letter-spacing:-.01em;
  word-break:break-all }
/* Statusen står i rubriken. Det vanliga ärendet är "hur går det?", och det
   ska inte kräva ett tryck. */
.acc__sub { display:block; margin-top:.125rem; font-size:.75rem; line-height:1.4;
  color:rgba(var(--ink-rgb),.58) }
.acc__chev { flex:0 0 auto; width:1.125rem; height:1.125rem; color:rgba(var(--ink-rgb),.35);
  stroke:currentColor; fill:none; stroke-width:2; stroke-linecap:round; stroke-linejoin:round;
  transform:rotate(90deg); transition:transform .2s }
.acc__chev.is-open { transform:rotate(-90deg) }
.acc__body { padding:0 .25rem 1.5rem }

/* Nyckel–värde utan kortram. Rader räcker; en ram runt varje uppgift gör
   sidan till en hög av lådor. */
.kv__row { display:flex; gap:1rem; padding:.4375rem 0 }
.kv__key { flex:0 0 4.5rem; font-size:.75rem; font-weight:600; text-transform:uppercase;
  letter-spacing:.06em; color:rgba(var(--ink-rgb),.5); padding-top:.125rem }
.kv__val { flex:1 1 auto; min-width:0; font-size:.875rem; line-height:1.5 }

/* Egna fältetiketter är användarens ord och kan vara en hel mening. En fast
   smal nyckelkolumn bryter dem till en trappa — på svarsskärmen står
   etiketten därför över värdet i stället för bredvid. */
.kv--stacked .kv__row { flex-wrap:wrap; align-items:flex-start;
  gap:.125rem .75rem; padding:.5rem 0 }
.kv--stacked .kv__key { flex:1 1 100%; padding-top:0; white-space:normal }
.kv--stacked .kv__val { flex:1 1 auto }

/* ============================ DNS-post ============================
   Vad posten ska vara, och vad som ligger där nu. Klarmarkeringen är en
   textetikett och inte en färgprick: den ska gå att läsa. */
.rec { padding:1rem 0; border-top:1px solid var(--line) }
.rec:first-of-type { border-top:none }
.rec__head { display:flex; align-items:center; gap:.5rem; margin-bottom:.5rem }
.rec__type { font-size:.6875rem; font-weight:700; letter-spacing:.06em;
  padding:.1875rem .4375rem; border-radius:.375rem;
  background:var(--surface-2); color:rgba(var(--ink-rgb),.66) }
.rec__what { flex:1 1 auto; font-size:.8125rem; color:rgba(var(--ink-rgb),.62) }
.rec__ok { font-size:.6875rem; font-weight:600; letter-spacing:.04em; color:var(--ok) }
.rec__todo { font-size:.6875rem; font-weight:600; letter-spacing:.04em; color:var(--accent) }
.rec--done .rec__type { opacity:.5 }

.rec__row { display:flex; align-items:flex-start; gap:.75rem; padding:.3125rem 0 }
.rec__key { flex:0 0 5rem; font-size:.75rem; color:rgba(var(--ink-rgb),.55); padding-top:.1875rem }
.rec__val { flex:1 1 auto; min-width:0; font-size:.875rem; line-height:1.45;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace; word-break:break-all }
/* Instruktionen är en mening, inte ett värde.

   .rec__val är monospace och bryter mitt i ord — rätt för en IP-adress
   eller en acme-sträng man ska kunna jämföra tecken för tecken, fel för
   en text man ska läsa. Raden bär därför brödtextens typsnitt, normal
   ordbrytning och lite mer luft över sig. */
.rec__row--how { margin-top:.375rem; align-items:flex-start }
.rec__row--how .rec__val {
  font-family:inherit; font-size:.8125rem; line-height:1.55;
  word-break:normal; color:rgba(var(--ink-rgb),.72);
}
.rec__row--how .rec__val b { color:var(--foreground); font-weight:600 }

.rec__note { margin-top:.5rem; padding-left:5.75rem; font-size:.75rem; line-height:1.5;
  color:var(--accent) }
.rec__copy { flex:0 0 auto; display:grid; place-items:center; width:2rem; height:2rem;
  margin:-.25rem -.25rem 0 0; border-radius:.5rem; color:rgba(var(--ink-rgb),.5) }
.rec__copy:active { background:var(--surface-2); color:var(--foreground) }
.rec__copy svg { width:1rem; height:1rem; stroke:currentColor; fill:none;
  stroke-width:2; stroke-linecap:round; stroke-linejoin:round }

/* ====================== stilväljare (startsektionen) ===============
   Utformningarna ritas i stället för att beskrivas. "Delad" säger inget
   förrän man sett att texten står till vänster och bilden till höger. */
/* Rubriken över nästa fält ska höra ihop med sitt eget fält och inte
   med rutnätet ovanför. Fältets vanliga marginal räcker inte när det som
   står över är fyra höga rutor. */
.field--layout { margin-bottom:1.75rem }
.styleGrid { display:grid; grid-template-columns:1fr 1fr; gap:.75rem }

/* Ett fält som följer direkt på ett rutnät av kort behöver mer luft än ett
   som följer på ett annat fält. Rutorna är höga och tar blicken, och en
   liten versal etikett tätt under dem läser som en bildtext till den
   sista rutan. */
.styleGrid + .field, .styleGrid + .gate, .styleGrid + .sectionTitle--plain {
  margin-top:1.75rem }
/* Stoppningen var 10 px runt om och 14 nedtill, och rutan lästes som en
   miniatyr med text intryckt i kanten snarare än som ett kort. Miniatyren
   inuti har dessutom en egen ram, så två kanter låg tio pixlar isär.

   14 px runt om och 16 nedtill: miniatyren får luft mot kortets kant, och
   etiketten står inte längre an mot den. Markeringsringen (is-on) ritas
   som en inre skugga på 2 px och behöver också en marginal att synas i. */
.styleCard { padding:.875rem .875rem 1rem; text-align:left;
  background:var(--card); border:1px solid var(--line);
  border-radius:var(--radius-card); transition:box-shadow .15s, border-color .15s }
.styleCard.is-on { border-color:transparent; box-shadow:inset 0 0 0 2px var(--primary) }
.styleCard__label { display:block; margin-top:.625rem; font-size:.875rem; font-weight:600;
  letter-spacing:-.01em }
.styleCard__desc { display:block; margin-top:.125rem; font-size:.6875rem; line-height:1.35;
  color:rgba(var(--ink-rgb),.55) }

/* Miniatyren. Staplarna är text, rutan är bild — inga ord, ingen skala att
   tolka, bara formen. */
.hp { position:relative; display:flex; flex-direction:column; justify-content:center;
  gap:.25rem; height:4.5rem; padding:.5rem; overflow:hidden;
  border-radius:.5rem; background:var(--surface-2) }
.hp__t { display:block; height:.375rem; width:70%; border-radius:999px;
  background:rgba(var(--ink-rgb),.45) }
.hp__t--sm { height:.25rem; width:45%; background:rgba(var(--ink-rgb),.25) }
.hp__btn { display:block; height:.3125rem; width:30%; border-radius:999px;
  margin-top:.125rem; background:var(--accent) }

/* Med bild: mörk yta och ljusa staplar, som en hero med bakgrundsbild. */
.hp--img { background:linear-gradient(160deg,#5b5b5b,#2a2a2a) }
.hp--img .hp__t { background:rgba(255,255,255,.85) }
.hp--img .hp__t--sm { background:rgba(255,255,255,.45) }
.hp--bottom { justify-content:flex-end }
.hp--center { align-items:center; text-align:center }
.hp--center .hp__t, .hp--center .hp__t--sm, .hp--center .hp__btn { margin-inline:auto }

.hp--plain { background:var(--surface-2); justify-content:flex-start }
/* Textställningens förhandsbilder står stilla i mitten och behöver
   luft i sidled för att kanten ska gå att se. */
.hp--pad { justify-content:center; padding:.625rem .75rem }
.hp--boxC { align-items:center }

/* Byggstenar för sektionsväljarens förhandsbilder. */
.hp--grid3 { display:grid; grid-template-columns:repeat(3,1fr); gap:.25rem;
  align-content:center }
.hp__box { display:block; height:1.75rem; border-radius:.25rem;
  background:rgba(var(--ink-rgb),.16) }
/* Siffror: en tjock rad överst är talet, en tunn under är meningen. */
.hp__num { display:block; height:1.75rem; border-radius:.25rem;
  background:rgba(var(--ink-rgb),.10);
  border-top:.5rem solid rgba(var(--ink-rgb),.32) }
.hp__card { display:block; height:1.75rem; border-radius:.25rem;
  background:rgba(var(--ink-rgb),.16);
  border-bottom:.4375rem solid rgba(var(--ink-rgb),.08) }
.hp__col3 { display:block; height:2.25rem; border-radius:.25rem;
  background:rgba(var(--ink-rgb),.12) }
.hp__col3--hi { background:var(--ink) }
.hp--rows { justify-content:center; gap:.3125rem }
.hp__row { display:block; height:.5rem; border-radius:.1875rem;
  background:rgba(var(--ink-rgb),.12) }

/* Rörelsevalen ritas som utgångsläget — där sektionen står innan den kommer
   på plats. Övergången här är bara till för att markeringen ska kännas
   levande när man byter val. */
.styleCard .hp { transition:opacity .25s ease, transform .25s ease }

.hp--split { flex-direction:row; align-items:center; gap:.5rem; background:var(--surface-2) }
.hp--split .hp__col { flex:1 1 0; display:flex; flex-direction:column; gap:.25rem }
.hp--split .hp__t { width:100% }
.hp--split .hp__t--sm { width:65% }
.hp--split .hp__img { flex:1 1 0; align-self:stretch; border-radius:.375rem;
  background:linear-gradient(160deg,#5b5b5b,#2a2a2a) }

/* ========================= länkväljare ============================
   En knapplänk pekar nästan alltid på en egen sida. Sidan väljs på en egen
   skärm i stället för i en meny som fälls ut i formuläret: sidorna kan vara
   hur många som helst, och en lista som växer nedåt skjuter undan allt
   under sig. */
.navrow--field { padding:.9375rem 1rem; border:1px solid var(--line);
  border-radius:var(--radius-control); background:var(--card) }

/* Länkfältet som en ruta.

   Ramen sitter på lådan, inte på delarna. Segmenten längst upp tappar sin
   egen ram och sina rundade hörn nedåt; raden under tappar sin helt och
   får en tunn linje som skiljetecken i stället. */
.linkBox { border:1px solid var(--line); border-radius:var(--radius-control);
  background:var(--card); overflow:hidden }
.linkBox .seg { border:0; border-radius:0; background:none;
  border-bottom:1px solid var(--line); padding:.375rem .375rem .4375rem }
.linkBox .navrow--field { border:0; border-radius:0; background:none }
.linkBox .ctrl { border:0; border-radius:0; background:none }
.linkBox .ctrl:focus { border:0 }
.linkBox:focus-within { border-color:rgba(var(--ink-rgb),.35) }
.navrow--field .navrow__label { font-size:1rem; font-weight:500 }
/* Samma ton som en platshållare i ett fält, inte ljusare. 30 % svart
   läses som avstängt; det här är en rad man ska trycka på. */
.navrow--field .navrow__label.is-empty { font-weight:400; color:rgba(var(--ink-rgb),.58) }

/* ========================= inkorg och chatt ======================= */


/* Här stod ett stapeldiagram: en stapel per dygn, ingen axel, inga
   siffror. Det ritades av ingen efter att statistiken fick sin kurva med
   två serier och en axel, och en regeluppsättning som heter samma sak som
   den som används är värre än ingen alls — den vann över kurvan i sidled
   och tryckte ihop den till nittiosex pixlar. Se .chart längre upp. */

/* Fältruta i miniatyren, för formulärets utformningar. */
.hp__f { display:block; height:.4375rem; border-radius:.125rem;
  background:rgba(255,255,255,.9); box-shadow:inset 0 0 0 1px rgba(var(--ink-rgb),.12) }

/* Hänglås före SSL-raden. Ikonen säger på en halv sekund det texten säger
   på en hel: att trafiken är krypterad. */
.lockIco { display:inline-block; width:.8125rem; height:.8125rem; margin-right:.25rem;
  vertical-align:-.0625rem; stroke:currentColor; fill:none; stroke-width:2;
  stroke-linecap:round; stroke-linejoin:round; color:var(--ok) }

/* ========================= sticky åtgärd ==========================
   Den enda knappen på en lista ska nås utan att man rullar till slutet.
   Sticky och inte fixed: fixed hade legat kvar när man bytte flik, och
   sticky hör till sitt innehåll — finns listan inte finns knappen inte.

   Toningen gör att raderna glider in under knappen i stället för att
   klippas av en kant. Den är transparent högst upp, så inget hoppar. */
/* Rubriken följer med när listan rullar. Den säger var man är, och en
   lista på fyrtio rader gör att man annars tappar det efter en tumme.

   Ut ur kroppens sidopadding och tillbaka in med egen: bakgrunden måste
   nå kanterna, annars rullar raderna fram i springorna vid sidan om. */
.screenHead {
  position:sticky; top:0; z-index:6;
  margin:0 -1.25rem; padding:.25rem 1.25rem .875rem;
  background:var(--background);
}
/* Rubriken behöver luft ned till flikraden. En rem räckte när rubriken
   stod ensam; med en rad tryckknappar direkt under läses de som ett
   block, och rubriken slutar säga vad den står över. */
.screenHead > .h1 { margin:.5rem 0 1rem }
.screenHead > .rowSplit { margin-bottom:1rem }
.screenHead > .rowSplit > .h1 { margin:0 }
/* Rubriken i verktygsraden bär en padding för underlängderna, se
   .rowSplit--tools .h1. Marginalen tar tillbaka den så raden inte växer —
   och måste stå efter regeln ovanför för att alls gälla. */
.screenHead > .rowSplit--tools > .h1 { margin-bottom:-.16em }
/* Rubrikraden med sök och åtgärd står tätare mot listan än en ensam
   rubrik gör: de hör ihop, och en rem emellan lät dem se ut som två
   saker på samma skärm. */
.screenHead > .rowSplit--tools { margin-bottom:.5rem }

/* Knappen ligger ovanpå listan, inte i den. Bakgrunden är hel: en
   toning som slutar halvvägs lät raderna lysa igenom knappens överkant.
   Toningen finns kvar, men ovanför knappen — där den ska mjuka övergången
   och inte ligga bakom något man ska kunna läsa. */
.stickyAction {
  position:sticky; z-index:5;
  bottom:var(--tabbarTotal);
  margin-top:.75rem; padding:.75rem 0 1rem;
  background:var(--background);
}
.body--noTabs .stickyAction { bottom:env(safe-area-inset-bottom) }
/* Utan innehåll ska den inte finnas.

   Publicera-knappen ritas bara när något väntar på att publiceras, men
   omslaget ritades alltid. Kvar blev ett tomt klistrat band på 1,75 rem
   med sin toning ovanför — en kant som låg och svävade över sista raden
   i listan utan att höra till något. */
.stickyAction:empty { display:none }
/* Mjuk och lång, inte kort och rak.

   1,75 rem rakt från genomskinligt till bakgrund gav en kant man såg —
   en tydlig skugga över sista raden i stället för en toning. Höjden är
   dubblad och stoppen ligger på en kurva: toningen är nästan osynlig
   större delen av vägen och tätnar först sista biten, som ljus gör. */
.stickyAction::before {
  content:""; position:absolute; left:0; right:0; bottom:100%; height:3.5rem;
  background:linear-gradient(to bottom,
    color-mix(in srgb, var(--background) 0%, transparent) 0%,
    color-mix(in srgb, var(--background) 12%, transparent) 30%,
    color-mix(in srgb, var(--background) 40%, transparent) 60%,
    color-mix(in srgb, var(--background) 78%, transparent) 85%,
    var(--background) 100%);
  pointer-events:none;
}
/* På skärmar utan flikrad finns ingen rad att lägga sig ovanför. */

/* ============================== kod ===============================
   Fast teckenbredd och radnummerlös enkelhet. Kod läses i kolumner, och
   ett proportionellt typsnitt gör indrag omöjliga att se. */
.code {
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:.8125rem; line-height:1.6; tab-size:2;
}
textarea.code { min-height:14rem; white-space:pre; overflow-wrap:normal; overflow-x:auto }

/* Referensen är till för att läsas, inte redigeras: egen yta, egen
   rullning, och den tar inte halva skärmen. */
.code--read {
  max-height:18rem; overflow:auto; padding:1rem;
  background:var(--surface-2); border-radius:var(--radius-card);
  color:rgba(var(--ink-rgb),.7); white-space:pre-wrap; word-break:break-word;
}

/* ============================== hem ===============================
   Ett skärmfullt, inte en lista att rulla igenom. Skärmen svarar på tre
   frågor i den ordning man ställer dem: går det bra, väntar något på mig,
   och är det som ligger ute aktuellt. */

/* Besökarkortet. Talen står ovanför kurvan, för siffran är svaret och
   kurvan är sammanhanget. */
/* Plusknappen i mitten av flikraden. Den byter inte vy utan gör något,
   och ska därför inte se ut som de andra fyra: en fylld cirkel i
   accentfärg, lyft över raden så tummen hittar den utan att titta. */

/* ============================= förslag ============================
   Sidan som förslaget skulle bli, i helskärm, med besluten längst ned.
   Ingen vaddring runt ramen: det man ser ska vara sidan och inte en bild
   av den i en ruta. */
/* =========================== förslagskort ==========================
   Ett förslag är tre saker: vad som ändras, från vad till vad, och varför.
   Förhandsgranskningen som ritade hela sidan i en iframe visade det fjärde
   — allt annat — och lät en söktitel som inte ens syns på sidan bli något
   man skulle godkänna på känsla.

   Korten ligger under varandra med besluten i varje. En lista där varje
   rad ska öppnas för att kunna avgöras är en lista man inte betar av. */
.suggList { display:flex; flex-direction:column; gap:.875rem }
.suggCard { display:flex; flex-direction:column; gap:.75rem }
.suggCard__head { display:flex; flex-direction:column; gap:.1875rem }
.suggCard__title { font-size:.9375rem; font-weight:600; line-height:1.35 }
.suggCard__meta {
  font-size:.75rem; color:rgba(var(--ink-rgb),.5);
}
/* Motiveringen. Genomgången skriver vilken siffra som ligger bakom, och
   det är den som avgör om resten är värd att läsa. */
.suggCard__why { font-size:.875rem; line-height:1.55; color:rgba(var(--ink-rgb),.66) }
.suggCard__done { font-size:.8125rem; line-height:1.5; color:rgba(var(--ink-rgb),.55) }
.suggCard__done a { color:var(--accent-to); cursor:pointer }
.suggCard__acts { display:flex; justify-content:flex-end; align-items:center; gap:.5rem }
.suggCard__acts .btn { width:auto }

/* Före och efter, under varandra och inte bredvid.

   Två spalter hade varit tydligare på ett skrivbord och omöjliga på en
   telefon: texterna är hela meningar, och två spalter om 170 px blir två
   staplar med ett ord på raden. Etiketterna Nu och Blir bär skillnaden i
   stället, och den gamla texten är dämpad så ögat landar på den nya. */
.diff {
  display:flex; flex-direction:column; gap:.375rem;
  padding:.75rem; border-radius:var(--radius-control);
  background:var(--surface); border:1px solid var(--line);
}
.diff__what {
  font-size:.6875rem; font-weight:600; letter-spacing:.06em;
  text-transform:uppercase; color:rgba(var(--ink-rgb),.5);
}
.diff__row { display:flex; gap:.5rem; align-items:baseline; font-size:.875rem; line-height:1.5 }
.diff__tag {
  flex:0 0 2.25rem; font-size:.6875rem; font-weight:600;
  color:rgba(var(--ink-rgb),.45); padding-top:.125rem;
}
.diff__text { min-width:0; overflow-wrap:anywhere }
.diff__row--from .diff__text { color:rgba(var(--ink-rgb),.5) }
.diff__row--to .diff__text { color:var(--foreground) }
.diff__code { margin:0; max-height:14rem; font-size:.75rem }

/* Inläggsplanen: en rad per inlägg, bilden till vänster.

   Bilden är kvadratisk och liten med flit. Den ska räcka för att känna
   igen motivet — det är den frågan man ställer om en bild i en plan — och
   inte så stor att tre inlägg blir en skärm man måste rulla igenom för att
   se hela veckan. Ett inlägg i sin fulla storlek ser man i redigeraren
   efter att man sagt ja.

   Tiden står överst och inte sist. Den är det som avgör om planen håller:
   texten går att skriva om, men ett inlägg på fel dag är fel inlägg. */
.planList { display:flex; flex-direction:column; gap:.5rem }
.plan {
  display:flex; gap:.75rem; align-items:flex-start;
  padding:.75rem; border-radius:var(--radius-control);
  background:var(--surface); border:1px solid var(--line);
}
.plan__img {
  flex:0 0 auto; width:4.5rem; height:4.5rem; object-fit:cover;
  border-radius:.5rem; background:var(--surface-2);
}
.plan__img--tom { display:block }
.plan__body { min-width:0; display:flex; flex-direction:column; gap:.25rem }
.plan__when {
  font-size:.6875rem; font-weight:600; letter-spacing:.04em;
  text-transform:uppercase; color:rgba(var(--ink-rgb),.5);
}
/* Den enda raden i kortet som bär en varning. Accenten och inte rött:
   inget är trasigt, tiden behöver bara rättas innan någon schemalägger.
   accenten står för "det här väntar på dig" i hela appen, och det är
   precis vad en tid som passerat gör. */
.plan__when--past { color:var(--accent-to) }
.plan__head {
  font-size:.9375rem; font-weight:600; line-height:1.35;
  color:var(--foreground); overflow-wrap:anywhere;
}
.plan__text {
  font-size:.8125rem; line-height:1.5;
  color:rgba(var(--ink-rgb),.75); overflow-wrap:anywhere;
}
/* Motiveringen sist och dämpad. Den läses av den som tvekar, inte av den
   som redan bestämt sig. */
.plan__why {
  font-size:.75rem; line-height:1.45; font-style:italic;
  color:rgba(var(--ink-rgb),.5); overflow-wrap:anywhere;
}

/* Väntande förslag, överst i assistenten. */

/* Knappen i ett svar som leder till förslaget. */
/* Det som blir kvar när knappen tagits bort. Samma plats i bubblan, men
   utan löfte om att något ska hända. */
.llm__done { display:block; margin-top:.75rem; font-size:.8125rem;
  color:rgba(var(--ink-rgb),.55) }

/* Egen rad, alltid. Som inline-flex flöt knappen in i sista textraden och
   klöv meningen på mitten — "upp." till vänster om den, resten ovanför. */
.llm__open {
  display:flex; width:fit-content; align-items:center; gap:.4375rem; margin-top:.75rem;
  padding:.5rem .875rem; border-radius:var(--radius-pill);
  background:var(--primary); color:var(--on-primary); font-size:.8125rem; font-weight:600;
}
.llm__open svg { width:.875rem; height:.875rem; stroke:currentColor; fill:none;
  stroke-width:2; stroke-linecap:round; stroke-linejoin:round }

/* ============================ assistent ===========================
   Ett samtal, inte en instrumentpanel. Flödet växer nedåt och fältet står
   still längst ned — samma form som varje chatt någon använt, vilket är
   hela poängen: den ska inte behöva förklaras. */
/* Kroppen är kolumnen, inte samtalet.

   Förr bar .llm en min-height räknad i dvh. Den blev längre än skärmen i
   appramen på skrivbordet, och då hamnade fältet under vikningen medan
   sticky höll det längst ned i en behållare ingen såg. En kolumn som
   fyller kroppen exakt har inget att sticka fast vid — och behöver det
   inte. */
.body--fill { display:flex; flex-direction:column; min-height:0 }

/* Vägen ut till sajten, i Hems list på telefon — se .bar--home i
   toppfältsavsnittet.

   Knappen bar förut adressen som text, utan ram: en adress är en väg och
   inte en åtgärd, och strecket under sa vad ramen annars hade sagt. Nu
   står ordet där i stället, och då är ramen rätt igen — det är en knapp
   som gör något, och den ska se ut som appens andra knappar.

   Skjuts åt höger med auto-marginal så rubriken Hem behåller vänsterkanten
   och knappen står vänster om klockan. */
/* Borta. Samma väg finns i brickraden ovanför skrivfältet, och där står
   den bredvid de andra ingångarna i stället för att trängas med klockan,
   kuggen och kontot i en rad på 390 punkter. Regeln står kvar tom-ish för
   att markupen finns kvar: knappen bär data-open-site och är fortfarande
   den enda vägen ut på skärmar där brickraden inte ritas. */
.homeSite { display:none }

/* Hems två flikar. Panelerna ligger båda i märkspråket och byts genom att
   döljas: samtalet med assistenten skulle annars börja om varje gång man
   tittat på en siffra. Den som är framme fyller vyn, som samtalet gjorde
   när det var ensamt här. */
.homePane { flex:1 1 auto; display:flex; flex-direction:column; min-height:0 }
.homePane[hidden] { display:none }
/* Siffrorna är en lista att rulla i, inte ett samtal som ska nå botten. */
/* Flikraden på Hem bär sitt eget avstånd. Den ligger i en body--fill, där
   marginalregeln för seg--tabs inte gäller: nästa element är en panel som
   fyller resten och inte ett stycke text. */
.llm { position:relative; flex:1 1 auto; display:flex; flex-direction:column; min-height:0 }

/* Släppytan. Ligger över samtalet men tar inte emot pekaren: gjorde den
   det skulle släppet landa på rutan i stället för på flödet under, och
   ingenting hända. */
.llm.is-drop::after {
  content:"Släpp bilden här";
  position:absolute; inset:0; z-index:2;
  display:grid; place-items:center;
  border:2px dashed rgba(var(--ink-rgb),.25); border-radius:var(--radius-card);
  background:color-mix(in srgb, var(--background) 82%, transparent);
  font-size:.875rem; color:rgba(var(--ink-rgb),.62);
  pointer-events:none;
}
/* Luften mellan bubblorna är vad som gör dem till repliker och inte till
   ett stycke. .625 rem räckte när flödet var två rader långt; med ett
   riktigt samtal la sig svaren mot varandra och det gick inte att se var
   ett slutade. */
.llm__log { flex:1 1 auto; display:flex; flex-direction:column; gap:1.375rem;
  padding:1rem 0 .5rem; overflow-y:auto; min-height:0 }

/* ---- innan samtalet börjat ----
   En fråga och ett fält mitt på skärmen, som varje annan assistent
   börjar. Loggen slutar fylla höjden, så att frågan, brickorna och
   fältet kan stå tillsammans i mitten; första repliken tar bort
   klassen och loggen växer till sitt vanliga flöde (se paintThread). */
.llm.is-empty { justify-content:center }
.llm.is-empty .llm__log { flex:0 0 auto; overflow:visible; padding:0 }
.llm__hello { display:flex; flex-direction:column; align-items:center;
  text-align:center; padding:0 1rem 1.25rem }
.llm__helloTitle { margin:0; font-size:1.5rem; font-weight:600;
  letter-spacing:-.02em; line-height:1.2 }
.llm__helloText { margin:.5rem 0 0; max-width:310px; font-size:.9375rem;
  line-height:1.5; color:rgba(var(--ink-rgb),.62) }
/* Brickorna centreras med resten; med ett samtal står de till vänster
   som förut, i linje med fältets kant. */
/* safe center: centrerat när brickorna ryms, vänsterställt när de inte
   gör det. Ren center låste raden kring mitten även när den var bredare
   än skärmen, och då låg de första brickorna utanför vänsterkanten utan
   att gå att rulla fram — Visa webbplats syntes aldrig på en telefon. */
.llm.is-empty .llm__above { justify-content:safe center }
/* På skrivbordet finns höjd nog för att fältet ska stå en aning över
   mitten, där ögat landar. Padding nedtill knuffar gruppen uppåt. */
body.is-app #dashboard-body .llm.is-empty { padding-bottom:12vh }

/* pre-wrap: assistenten svarar i stycken och punktlistor, och utan det
   här blir en uppställning en enda lång mening. Radbrytningarna är dess
   enda sätt att strukturera — vi kastar inte bort dem. */
.llm__m { max-width:85%; padding:.75rem .9375rem; border-radius:1.125rem;
  font-size:.9375rem; line-height:1.5; white-space:pre-wrap;
  overflow-wrap:anywhere }
/* Hela hörn, inga stjärtar.

   Bubblorna hade ett avhugget hörn mot sin egen sida — meddelandeappens
   grepp för att peka ut vem som talar. Här pekar redan sidan och färgen
   ut det, och det som blev kvar av greppet lästes inte som en stjärt utan
   som ett hörn där rundningen glömts bort. */
.llm__m--ai { align-self:flex-start; background:var(--card);
  border:1px solid var(--line) }
.llm__m--me { align-self:flex-end; background:var(--primary); color:var(--on-primary) }

/* ---- genomgångens tal ----
   Ligger inuti assistentens bubbla, under meningarna som förklarar dem.
   Ett eget kort hade gjort dem till en tabell igen, och en tabell är
   precis det man slutar läsa.

   white-space:normal: bubblan står på pre-wrap för att bevara radbrytningar
   i det assistenten skriver, och det ärvs ned hit där märkspråket sköter
   brytningarna själv. Utan den blev varje mellanslag i etiketten en rad. */
/* Rutnät och inte flex. Med flex fick raderna olika spaltbredd beroende på
   hur många tal som fanns, och ett tredje tal på egen rad hamnade snett
   under det första — tre tal ska stå i pelare, inte i en hög. */
.rapTal { display:grid; grid-template-columns:repeat(2, minmax(0, 1fr));
  gap:.75rem .75rem; margin-top:.875rem;
  padding-top:.75rem; border-top:1px solid var(--line); white-space:normal }
.rapTal__t { min-width:0 }
.rapTal__l { display:block; font-size:.6875rem; letter-spacing:.02em;
  color:rgba(var(--ink-rgb),.55) }
.rapTal__v { display:block; margin-top:.125rem; font-size:1.25rem;
  font-weight:600; letter-spacing:-.02em; line-height:1.1;
  font-variant-numeric:tabular-nums }
/* Jämförelsen är liten med flit. Talet är det man ser, riktningen det man
   läser efteråt — tvärtom hade gjort varje vecka till en dom. */
.rapTal__d { display:flex; align-items:center; gap:.1875rem; margin-top:.1875rem;
  font-size:.6875rem; line-height:1.3; color:rgba(var(--ink-rgb),.55) }
.rapTal__ico { width:.75rem; height:.75rem; flex:0 0 auto;
  stroke:currentColor; fill:none; stroke-width:2.5;
  stroke-linecap:round; stroke-linejoin:round }
/* Grönt bara när rörelsen är bra. Rött används inte: en rapport som
   färgar en rad röd varje vecka blir en tillrättavisning, och det som
   inte går bra står redan utskrivet i meningarna ovanför. */
.rapTal__d.is-good { color:var(--ok) }

/* Tre prickar medan svaret skrivs. Rörelse säger "det händer något" utan
   att lova när — en förloppsindikator hade lovat det. */
.llm__m--wait { display:flex; gap:.25rem; padding:.9375rem }
/* Väntan med ett ord om varför. Texten står före prickarna, för det
   är den som svarar på frågan man ställer sig. */
.llm__m--waitText { align-items:center; padding:.75rem .9375rem;
  font-size:.8125rem; color:rgba(var(--ink-rgb),.62) }
.llm__m--waitText::before { content:attr(data-note); margin-right:.375rem }
/* Assistentens resonemang medan det pågår.
   Samma väntruta som ovan, men texten är flera meningar i stället för ett
   ord: den lägger sig därför på en egen rad med prickarna under, i stället
   för att trycka ihop dem mot kanten. Måttet hindrar en lång tanke från
   att bli en rad man måste läsa i sidled. */
.llm__m--think { flex-wrap:wrap; max-width:min(34rem,88%) }
.llm__m--think::before {
  flex:1 1 100%; margin:0 0 .375rem; line-height:1.5;
  white-space:pre-wrap; word-break:break-word;
}
.llm__m--wait span { width:.375rem; height:.375rem; border-radius:50%;
  background:rgba(var(--ink-rgb),.3); animation:llmDot 1.2s infinite }
.llm__m--wait span:nth-child(2) { animation-delay:.15s }
.llm__m--wait span:nth-child(3) { animation-delay:.3s }
@keyframes llmDot { 0%,60%,100% { opacity:.3 } 30% { opacity:1 } }
@media (prefers-reduced-motion:reduce) { .llm__m--wait span { animation:none } }

/* Rensa samtalet. Ligger överst i loggen, där samtalet börjar, och är
   avsiktligt anspråkslös: den som vill fortsätta läsa ska inte behöva
   passera en knapp som ser ut som ett beslut. */

/* Förslagen är exempel, inte knappar man måste trycka på. De rullar i
   sidled så de aldrig tvingar fram en andra rad. */
.llm__hints { flex:0 0 auto; display:flex; gap:.375rem; overflow-x:auto;
  padding:.5rem 0; scrollbar-width:none }
.llm__hints::-webkit-scrollbar { display:none }
.llm__hint { flex:0 0 auto; padding:.4375rem .75rem; border-radius:var(--radius-pill);
  background:var(--surface-2); font-size:.75rem; color:rgba(var(--ink-rgb),.7);
  white-space:nowrap }
.llm__hint:active { background:var(--line) }

/* Vägen till de färdiga frågorna. Ett piller som de andra, men med
   märkets ikon före ordet: det är en väg vidare och inte en fråga att
   trycka på, och skillnaden ska synas innan man läst etiketten. */

/* ============================ promptlistan ========================
   En rad per fråga: handtaget till vänster, frågan i mitten, krysset till
   höger. Frågan är knappen — hela mittytan — för att träffytan ska vara
   raden och inte ett ord i den. */
.promptList {
  display:flex; flex-direction:column;
  background:var(--card); border:1px solid var(--line);
  border-radius:1rem; overflow:hidden;
}
.promptRow { display:flex; align-items:stretch; min-height:3.25rem }
.promptRow + .promptRow { border-top:1px solid var(--line) }
.promptRow__grip {
  flex:0 0 auto; display:grid; place-items:center; width:2.75rem;
  color:rgba(var(--ink-rgb),.3); cursor:grab; touch-action:none;
}
.promptRow__grip svg { width:1.25rem; height:1.25rem; fill:currentColor; stroke:none }
.promptRow__use {
  flex:1 1 auto; min-width:0; padding:.875rem .5rem .875rem 0;
  text-align:left; font-size:.9375rem; line-height:1.45; color:inherit;
}
.promptRow__use:active { color:var(--accent-to) }
.promptRow__x {
  flex:0 0 auto; display:grid; place-items:center; width:2.75rem;
  color:rgba(var(--ink-rgb),.35);
}
.promptRow__x svg {
  width:.9375rem; height:.9375rem; stroke:currentColor; fill:none;
  stroke-width:2; stroke-linecap:round;
}
.promptRow__x:active { color:var(--danger) }
.promptRow--ghost { opacity:.35 }
.promptRow--chosen { background:var(--surface) }

/* Raden är ett fält, inte tre lådor bredvid varandra.

   Ramen sitter på formuläret och knapparna ligger inuti den. Fältet självt
   är genomskinligt och ramlöst — annars ser man en kant runt texten och en
   till runt alltihop, och plusknappen ser ut att stå utanför något den
   ligger i.

   flex-end: när texten växer till flera rader stannar knapparna vid den
   nedersta, som i en meddelandeapp — de ska inte glida upp och hamna mitt
   i texten. */
/* Raden ovanför skrivfältet. Vänsterställd, för det är där blicken står
   när man just skrivit klart en rad och tittar upp.

   En rad, alltid. Radbrytning gjorde att raden ibland var en våning hög
   och ibland två, och skrivfältet under flyttade sig med den. Ryms inte
   brickorna rullar de i sidled i stället — en rad som fortsätter utanför
   kanten säger "det finns mer åt det hållet" utan att flytta något.

   Ingen mask i högerkanten längre. Den tonade ut sista brickan för att
   säga "det finns mer", men låg kvar även när man rullat till slutet
   och lästes då som en skugga som föll från skrivfältet över raden.
   Antydan om mer åt höger ges i stället av att raden går ända ut till
   skärmkanten på telefonen (se mediefrågan nedanför): en bricka som
   klipps av kanten själv säger det utan att något läggs ovanpå. */
.llm__above {
  flex:0 0 auto; display:flex; flex-wrap:nowrap;
  justify-content:flex-start; align-items:center; gap:.375rem;
  margin-bottom:.5rem;
  overflow-x:auto; overflow-y:hidden;
  scrollbar-width:none;
  /* Radens egen rullning ska stanna i raden, inte kedjas vidare till
     sidan bakom — samma skäl som periodraden i statistiken. */
  overscroll-behavior-x:contain;
}
.llm__above::-webkit-scrollbar { display:none }
/* På telefonen bryter raden ut ur kroppens sidostoppning och bär den
   själv, så brickorna börjar i linje med allt annat men rullar ända ut
   till skärmkanten. Den bricka som inte får plats klipps av kanten, och
   det är hela antydan: rullar man vidare kommer resten. Utan utbrytet
   stannade raden 1,25 rem innan kanten, och en bricka som slutar mitt i
   luften ser ut som ett fel snarare än som en fortsättning. */
@media (max-width:1099px) {
  .llm__above {
    margin-left:-1.25rem; margin-right:-1.25rem;
    padding-left:1.25rem; padding-right:1.25rem;
    scroll-padding-inline:1.25rem;
  }
}
/* Brickorna krymper inte. Utan detta klämmer flex ihop dem till oläsliga
   stumpar i stället för att låta raden rulla. */
.llm__above > * { flex:0 0 auto }
/* Behållarna för kön och rapporterna är tomma tills något finns. Utan den
   här raden bar de ändå radens gap och lämnade hål till vänster om
   Projektminne. */
.llm__above > #llm-queue:empty,
.llm__above > #llm-reports:empty { display:none }

/* Vägen in i minnet. Ghost och liten: den ska gå att hitta utan att tävla
   med fältet under. Accenten sitter i tecknet och inte i ytan — det gör
   den till något man kan öppna, inte något man borde. */
.memChip {
  display:inline-flex; align-items:center; gap:.4375rem;
  padding:.4375rem .875rem .4375rem .6875rem;
  border-radius:var(--radius-pill);
  background:transparent; color:rgba(var(--ink-rgb),.7);
  font-size:.8125rem; font-weight:500; letter-spacing:-.005em;
  box-shadow:inset 0 0 0 1px rgba(var(--ink-rgb),.16);
  cursor:pointer;
}
.memChip:hover { color:var(--foreground);
  box-shadow:inset 0 0 0 1px rgba(var(--ink-rgb),.3) }
.memChip:active { background:rgba(var(--ink-rgb),.05) }
.memChip__ico { flex:0 0 auto; width:1.0625rem; height:1.0625rem;
  color:var(--accent); stroke:currentColor; fill:none; stroke-width:2;
  stroke-linecap:round; stroke-linejoin:round }

/* Antalet står där de andra brickorna har sitt tecken: samma plats, samma
   storlek, men fylld i stället för streckad. Det är den enda av de tre som
   säger något som ändrar sig, och den enda som bär färg som yta. */
.memChip__n {
  flex:0 0 auto; display:grid; place-items:center;
  /* Samma mått som tecknet bredvid, annars blir den ena brickan högre än
     de andra — stoppningen är gemensam och det är innehållet som sätter
     höjden. */
  width:1.0625rem; height:1.0625rem; border-radius:50%;
  background:var(--accent); color:#fff;
  font-size:.6875rem; font-weight:700; font-style:normal;
  font-variant-numeric:tabular-nums;
}
/* Brickan med utkast får kantens accent också. Den skiljer sig från de två
   andra på ett sätt som spelar roll: här väntar något på ett beslut. */
.memChip--forslag { box-shadow:inset 0 0 0 1px rgba(177,95,44,.5) }
.memChip--forslag:hover { box-shadow:inset 0 0 0 1px var(--accent) }

/* Uppmaningen. Fylld där de andra är konturerade — de är ställen man går
   till, den här är något som ska göras, och skillnaden ska synas utan att
   brickan byter storlek. Tecknet ärver textens färg i stället för att bära
   accenten: på en accentfärgad yta hade accenten försvunnit. */
.memChip--cta {
  background:var(--accent); color:#fff; box-shadow:none;
}
.memChip--cta:hover { background:var(--accent-to); color:#fff; box-shadow:none }
.memChip--cta .memChip__ico { color:currentColor }
.memChip--cta[hidden] { display:none }

.llm__bar { position:relative;
  flex:0 0 auto; display:flex; gap:.25rem; align-items:flex-end;
  margin:.5rem 0 .375rem; padding:.3125rem .3125rem .3125rem .5rem;
  border:1px solid var(--line); border-radius:1.5rem;
  background:var(--card); transition:border-color .2s }

/* Ingen toning när raden svävar över en sajt.

   Toningen finns för att ett samtal som rullar under fältet ska tona ut i
   stället för att klippas tvärt. I telefonens förhandsgranskning finns
   inget samtal under fältet — där ligger hemsidan, och en remsa i appens
   bakgrundsfärg tvärs över den är inte en mjuk övergång utan en fläck. */
.llm--float .llm__bar::before { display:none }

/* Toningen över skrivfältet. Samma grepp som över Publicera-knappen i
   listorna, se .stickyAction::before, och av samma skäl: flödet rullar
   under fältet, och en rad text som tar tvärt slut mot kanten läses som
   avklippt i stället för som fortsättning.

   Slutfärgen skrivs som color-mix mot bakgrunden och inte som
   "transparent" — transparent är genomskinligt SVART, och en toning dit
   går genom grått på en ljus bakgrund. Samma sak som i .stickyAction.

   bottom:calc(100% + 1px) och inte bottom:100%: ett absolut placerat barn
   mäter mot förälderns INNERKANT, så 100% slutade ovanpå radens egen
   överkant och målade över den med bakgrundsfärgen. Ramen fanns runt om
   men syntes bara på tre sidor. Den extra pixeln är ramen. */
.llm__bar::before {
  content:""; position:absolute; left:0; right:0; bottom:calc(100% + 1px); height:3.5rem;
  /* Samma kurva som .stickyAction::before, se den. */
  background:linear-gradient(to bottom,
    color-mix(in srgb, var(--background) 0%, transparent) 0%,
    color-mix(in srgb, var(--background) 12%, transparent) 30%,
    color-mix(in srgb, var(--background) 40%, transparent) 60%,
    color-mix(in srgb, var(--background) 78%, transparent) 85%,
    var(--background) 100%);
  pointer-events:none;
}
.llm__bar:focus-within { border-color:rgba(var(--ink-rgb),.35) }

/* Höjden sätts av JS medan man skriver.

   min-height måste nollställas: textarea.ctrl har sex rems minsta höjd
   eftersom en textarea i ett formulär är ett fält man skriver stycken i.
   Här börjar man på en rad och växer därifrån. */
textarea.llm__input { flex:1 1 auto; min-width:0; min-height:0;
  border:0; background:none; border-radius:0; padding:.5rem .25rem;
  resize:none; overflow-y:hidden; line-height:1.4 }
textarea.llm__input:focus { border:0 }

.llm__plus, .llm__talk, .llm__mute, .llm__send { flex:0 0 auto;
  display:grid; place-items:center; width:2.25rem; height:2.25rem; border-radius:50% }
/* Skicka bär accentfärgen — samma orange som frontness.se. Vitt bläck på
   den i båda teman: accenten är mörk nog i ljust läge och lyser i mörkt. */
.llm__send { background:var(--accent); color:#fff }
.llm__plus, .llm__talk, .llm__mute { background:none; color:rgba(var(--ink-rgb),.62) }
/* Lite mer luft före Skicka än mellan de andra. Mikrofonen och den fyllda
   knappen läses annars som ett par, och den som siktar på en av dem träffar
   nästan den andra. Marginalen och inte radens gap: övriga avstånd ska stå
   kvar som de är. */
.llm__talk { margin-right:.25rem }

.llm__x { flex:0 0 auto; display:grid; place-items:center;
  width:2.25rem; height:2.25rem; border-radius:50%;
  background:none; color:rgba(var(--ink-rgb),.62) }
.llm__x svg { width:1.125rem; height:1.125rem; stroke:currentColor; fill:none;
  stroke-width:2; stroke-linecap:round; stroke-linejoin:round }
.llm__x:active { transform:scale(.9) }
.llm__x[hidden], .llm__wave[hidden] { display:none }

/* Fyra streck som andas i takt. Ingen riktig vågform: den hade krävt att
   ljudet analyserades för att säga samma sak — att appen lyssnar. */
.llm__wave { flex:0 0 auto; display:flex; align-items:center; gap:.1875rem;
  height:2.25rem; padding:0 .25rem }
.llm__wave i { display:block; width:.1875rem; height:.375rem; border-radius:999px;
  background:var(--accent); animation:llmWave 1s ease-in-out infinite }
.llm__wave i:nth-child(2) { animation-delay:.15s }
.llm__wave i:nth-child(3) { animation-delay:.3s }
.llm__wave i:nth-child(4) { animation-delay:.45s }
@keyframes llmWave {
  0%, 100% { height:.375rem; opacity:.55 }
  50%      { height:1.125rem; opacity:1 }
}
@media (prefers-reduced-motion: reduce) {
  .llm__wave i { animation:none; height:.75rem }
}

.llm__plus svg, .llm__talk svg, .llm__mute svg, .llm__send svg {
  width:1.125rem; height:1.125rem;
  stroke:currentColor; fill:none; stroke-width:2;
  stroke-linecap:round; stroke-linejoin:round }
.llm__plus svg { width:1.25rem; height:1.25rem }
.llm__plus:active, .llm__talk:active, .llm__mute:active,
.llm__send:active { transform:scale(.9) }
/* ---- talläget ----
   Samtalet tar över raden, inte skärmen. Hemsidan ska synas medan man
   pratar om den — det är hela poängen med att prata om den. Raden byter
   innehåll och behåller sin höjd: kryss, ett pulserande streck, ett
   besked om vad som händer och en knapp för att tysta mikrofonen.

   Det assistenten säger hamnar i samtalet, samma ställe som svaren på det
   man skriver. Talat och skrivet är samma samtal. */
.llm__bar.is-talking { border-color:var(--accent);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--accent) 16%, transparent) }
.llm__bar.is-talking .llm__plus,
.llm__bar.is-talking .llm__input,
.llm__bar.is-talking .llm__talk,
.llm__bar.is-talking .llm__send { display:none }

/* Statusraden i talläget.

   Raden är align-items:flex-end — knapparna ska stanna vid nedersta
   textraden när man skriver. En <span> med ren text har ingen egen höjd
   att bottna med, så den la sig i underkant medan krysset, vågen och
   mikrofonen stod på sina 2,25 rem. Texten hängde alltså lägre än allt
   annat på raden.

   Egen höjd och centrerad i den: då delar den mittlinje med resten. */
.llm__state { flex:1 1 auto; min-width:0; padding:0 .25rem;
  display:flex; align-items:center; min-height:2.25rem;
  font-size:.875rem; font-weight:500; color:rgba(var(--ink-rgb),.66);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis }
.llm__state[hidden], .llm__mute[hidden] { display:none }

/* Avstängd mikrofon syns, annars pratar man i en knapp som inte lyssnar. */
.llm__mute.is-off { background:var(--danger); color:#fff }
/* busy() sätter bara en klass — knappen har ingen etikett att byta ut. */
.llm__plus.is-busy { opacity:.45 }

/* Bilagan ovanför raden, som en liten etikett på det man är på väg att
   skicka. */
.llm__att { display:flex; align-items:center; gap:.5rem; margin-bottom:.375rem;
  padding:.3125rem .5rem .3125rem .3125rem; border-radius:.75rem;
  background:var(--surface-2); width:fit-content; max-width:100% }
.llm__att img { width:2rem; height:2rem; border-radius:.5rem; object-fit:cover;
  max-width:none; flex:0 0 auto }
.llm__attText { flex:1 1 auto; min-width:0; font-size:.75rem;
  color:rgba(var(--ink-rgb),.66); white-space:nowrap; overflow:hidden;
  text-overflow:ellipsis }
.llm__attX { flex:0 0 auto; width:1.25rem; height:1.25rem; border:0; padding:0;
  border-radius:50%; background:rgba(var(--ink-rgb),.12); color:rgba(var(--ink-rgb),.7);
  font-size:.875rem; line-height:1; cursor:pointer }

/* ====================== frågor och svar ==========================
   Redigeras på plats. Frågan är en rad, svaret flera, och raderna hålls
   isär av luft i stället för av kanter — en lista med sex boxar i box ser
   ut som ett formulär man måste fylla i, inte som något man får lägga
   till om man vill. */
.qa__row { padding:.875rem 0; border-bottom:1px solid var(--line) }
.qa__row:first-child { padding-top:0 }
.qa__row:last-child { border-bottom:0; padding-bottom:0 }
.qa__head { display:flex; align-items:center; gap:.5rem }
.qa__q { flex:1 1 auto; min-width:0; font-weight:600 }
.qa__a { margin-top:.5rem; min-height:4.5rem }

/* ========================= textredigerare =========================
   En knapprad och en yta att skriva i. Formen följer .ctrl, så fältet ser
   ut som de andra fälten i formuläret och inte som ett främmande verktyg
   någon monterat in. */
/* overflow:clip, inte hidden.

   hidden gör elementet till en rullbehållare, och en klistrad rad inuti
   en sådan klistrar sig mot den behållaren i stället för mot skärmen. Raden
   såg alltså klistrad ut i koden och rullade ändå bort med texten. clip
   klipper hörnen likadant utan att skapa någon rullning. */
.rte { border:1px solid var(--line); border-radius:var(--radius-control);
  background:var(--card); overflow:clip }
.rte:focus-within { border-color:rgba(var(--ink-rgb),.35) }

/* Raden är klistrad: en artikel blir lång, och knappar man måste rulla
   tillbaka till för att sätta en rubrik används inte.

   Den fastnar under topplisten och inte i skärmkanten. Med top:0 hade den
   glidit in bakom listen och blivit halvt dold precis när man behöver den. */
.rte__bar {
  position:sticky; top:calc(3.5rem + var(--top)); z-index:2;
  display:flex; gap:.125rem; padding:0 .375rem;
  background:var(--surface); border-bottom:0 solid var(--line);
}

/* Raden fälls fram när fältet används.

   En verktygsrad över varje textfält är sju knappar man inte behöver
   förrän man skriver. Höjden animeras i stället för att döljas rakt av,
   så texten under inte hoppar. */
/* Noll höjd betyder noll: en kantlinje på ett hopfällt element är en
   grå streck som ser ut som ett fel. */
.rte__bar { max-height:0; overflow:hidden; transition:max-height .18s ease,
  padding .18s ease }
.rte:focus-within .rte__bar { max-height:3rem; padding:.375rem;
  border-bottom-width:1px }

.rte__b {
  display:grid; place-items:center; width:2rem; height:2rem; flex:0 0 auto;
  border-radius:.5rem; font-size:.8125rem; color:rgba(var(--ink-rgb),.7);
}
.rte__b:active { background:rgba(var(--ink-rgb),.08) }
.rte__b svg { width:1.0625rem; height:1.0625rem; stroke:currentColor; fill:none;
  stroke-width:1.9; stroke-linecap:round; stroke-linejoin:round }

/* Lägre än ett blogginlägg. Fältet växer med texten, och en tom ruta som
   tar en tredjedel av skärmen säger att man förväntas skriva mycket. */
.rte__body {
  padding:.9375rem 1rem; min-height:8rem;
  font-size:1rem; line-height:1.65; outline:none;
}
.rte__body:empty::before {
  content:attr(data-placeholder); color:rgba(var(--ink-rgb),.3); pointer-events:none;
}

/* Anteckningen i minnet: text rakt på sidan.

   Inget kort och ingen ram. Skärmen ÄR anteckningen, och en ruta i en
   ruta sa bara att texten var text. Sorten står som en liten rad
   ovanför med sin prick, som en väljare utan att se ut som en.
   memNote och inte note: .note fanns redan, ett kort med bakgrund. */
.noteKind { display:flex; align-items:center; gap:.5rem; margin-top:1.5rem }
.noteKind .memGrupp__prick { flex:none }
.noteKind select, .noteKind__label {
  appearance:none; -webkit-appearance:none; background:transparent; border:0;
  padding:0; margin:0; font:inherit; font-size:.6875rem; font-weight:600;
  letter-spacing:.08em; text-transform:uppercase;
  color:rgba(var(--ink-rgb),.6); cursor:pointer;
}
.noteKind select:focus-visible { outline:2px solid var(--accent); outline-offset:3px; border-radius:2px }
.noteKind + .field__hint { margin-top:.375rem }
.memNote {
  margin-top:1rem; min-height:5rem; padding:0; font-size:1.125rem; line-height:1.6;
  outline:none; white-space:pre-wrap; overflow-wrap:anywhere;
  caret-color:var(--accent); color:rgba(var(--ink-rgb),1);
}
.memNote--still { min-height:0 }
.memNote:empty::before {
  content:attr(data-placeholder); color:rgba(var(--ink-rgb),.3); pointer-events:none;
}
.memNote + .field__hint { margin-top:.625rem }

/* Autopilotens grenar: rattarna under reglaget, avskilda med en linje. */
.autoBox__inner { margin-top:.75rem; padding-top:.25rem; border-top:1px solid var(--line) }
.autoBox__inner .switchRow:first-child { padding-top:.75rem }
/* Texten inuti ska se ut som texten kommer att se ut, i mindre skala.
   Att skriva i ett fält där allt är lika stort och sedan upptäcka
   rubrikerna först i förhandsgranskningen är att skriva i blindo. */
.rte__body > :first-child { margin-top:0 }
.rte__body h2 { font-size:1.25rem; font-weight:600; margin:1.25em 0 .35em;
  letter-spacing:-.02em }
.rte__body h3 { font-size:1.0625rem; font-weight:600; margin:1.1em 0 .3em }
.rte__body p { margin:0 0 .9em }
/* Punkterna tillbaka. ul { list-style:none } är nollställt globalt för
   appens egna listor, och redigeraren ärvde det: en punktlista såg ut som
   indragna stycken, alltså som ingenting. */
.rte__body ul { margin:0 0 .9em; padding-left:1.25em; list-style:disc }
.rte__body ol { margin:0 0 .9em; padding-left:1.35em; list-style:decimal }
.rte__body li { margin-bottom:.2em }
.rte__body li::marker { color:rgba(var(--ink-rgb),.55) }
.rte__body img { display:block; width:100%; height:auto; margin:1em 0;
  border-radius:.5rem }
.rte__body a { color:var(--accent) }
.rte__body blockquote { margin:1em 0; padding-left:.875rem;
  border-left:2px solid var(--accent); color:rgba(var(--ink-rgb),.7) }

/* Miniatyrer för sidhuvudets och sidfotens mallar. Logotypen är en rundad
   kloss, menyn tre streck — formen är hela budskapet. */
.hp--head { flex-direction:row; align-items:center; justify-content:space-between;
  height:3.25rem; background:var(--surface-2) }
.hp--headRev { flex-direction:row-reverse }
.hp--headCol { flex-direction:column; align-items:center; justify-content:center; gap:.375rem;
  height:4rem }
.hp--headLeft { align-items:flex-start }
.hp__logo { display:block; width:1.75rem; height:.5rem; border-radius:.1875rem;
  background:rgba(var(--ink-rgb),.55); flex:0 0 auto }
/* En person i miniatyren: en cirkel och en rad bredvid. Cirkeln är det
   enda som skiljer Kontaktpersoner från Kontakt i väljaren, så den ska
   synas som en cirkel också vid tre centimeters bredd. */
.hp__pers { display:flex; align-items:center; gap:.3125rem }
.hp__ava { display:block; flex:0 0 auto; width:.75rem; height:.75rem;
  border-radius:999px; background:rgba(var(--ink-rgb),.35) }
.hp--split .hp__pers .hp__t { flex:1 1 auto; width:auto }

.hp__menu { display:flex; gap:.25rem; align-items:center }
.hp__menu--c { justify-content:center }
.hp__menu i { display:block; width:.5rem; height:.25rem; border-radius:999px;
  background:rgba(var(--ink-rgb),.3) }

.hp--foot { background:#2a2a2a; height:4.5rem }
.hp--foot .hp__logo { background:rgba(255,255,255,.8) }
.hp--foot .hp__menu i { background:rgba(255,255,255,.45) }
.hp--foot .hp__t { background:rgba(255,255,255,.7) }
.hp--foot .hp__t--sm { background:rgba(255,255,255,.35) }
.hp__col--end { align-items:flex-end }
.hp--split .hp__col--end .hp__t--sm { width:70% }

/* Galleriets och tidslinjens miniatyrer i sektionsväljaren. Galleriet visar
   olika höga rutor — det är hela skillnaden mot Kort — och tidslinjen en
   smal spalt före varje rad. */
.hp--gal { flex-direction:row; align-items:flex-start; gap:.25rem }
.hp__gi { flex:1 1 0; height:1.5rem; border-radius:.1875rem;
  background:linear-gradient(160deg,#5b5b5b,#2a2a2a) }
.hp__gi--tall { height:2.25rem }
.hp__row--year { position:relative }
.hp__row--year::before { content:""; position:absolute; left:0; top:50%;
  width:22%; height:2px; margin-top:-1px; border-radius:1px;
  background:rgba(var(--ink-rgb),.45) }

/* Före och efter: EN ruta med en linje i, inte två rutor. Två rutor är
   galleriets bild, och skillnaden mellan de två typerna är just att den
   här lägger bilderna på varandra. Halvorna skiljs åt med olika ljus, så
   att linjen läses som en gräns mellan två bilder och inte som en rand. */
.hp__ba { position:relative; align-self:stretch; width:100%; overflow:hidden;
  border-radius:.375rem; background:linear-gradient(160deg,#5b5b5b,#2a2a2a) }
.hp__ba::before { content:""; position:absolute; inset:0 0 0 50%;
  background:linear-gradient(160deg,#8f8f8f,#4a4a4a) }
.hp__ba::after { content:""; position:absolute; top:0; bottom:0; left:50%;
  width:2px; margin-left:-1px; background:rgba(255,255,255,.9) }

/* Uppslaget: två former som går omlott. Överlappet är hela sektionen, så
   det är det enda miniatyren behöver visa — rutan måste ligga ÖVER
   bilden och sticka ut på ena sidan, annars är det två rutor bredvid
   varandra, vilket redan är Bild + text. */
.hp--sprd { position:relative; display:block }
.hp__sprdImg { position:absolute; left:0; top:14%; width:68%; height:72%;
  border-radius:.375rem; background:linear-gradient(160deg,#5b5b5b,#2a2a2a) }
.hp__sprdBox { position:absolute; right:0; top:32%; width:46%; height:40%;
  border-radius:.375rem; background:rgba(var(--ink-rgb),.14);
  box-shadow:0 .25rem .75rem rgba(0,0,0,.28);
  border:1px solid rgba(var(--ink-rgb),.10) }

/* Löpande rad: en bred stapel som går ut genom båda kanterna. Att den är
   avskuren är poängen — raden fortsätter bortom det man ser. */
.hp__mq { display:block; width:130%; height:26%; border-radius:2px;
  background:repeating-linear-gradient(90deg,
    rgba(var(--ink-rgb),.42) 0 18%, transparent 18% 24%) }

/* ============================= räknare ============================
   En siffra på fliken och på klockan. Den säger hur mycket som väntar —
   en prick hade bara sagt att något gör det, och skillnaden mellan ett
   och tolv är hela poängen. */
.tab__count, .bar__count {
  position:absolute; display:grid; place-items:center;
  /* Rund vid en och två siffror, avlång först vid tre. Den gamla
     vaddringen gjorde även en etta 21 px bred mot 18 hög — en oval som
     såg ut som ett misstag bredvid en cirkelrund ikon. */
  min-width:1.125rem; height:1.125rem; padding:0 .1875rem;
  border-radius:999px; background:var(--accent); color:#fff;
  font-size:.625rem; font-weight:700; line-height:1;
  font-variant-numeric:tabular-nums;
}
.tab { position:relative }
/* Fäst vid ikonens övre högra hörn, inte svävande i flikens hörn. Ringen
   i bakgrundsfärgen klipper ut den ur ikonen så den läses som en
   markering på den och inte som ett löst element. */
/* Mätt från flikens mitt och inte från dess överkant.
 *
 * Med en etikett under sig satt ikonen i övre halvan, och .5 rem från
 * knappens topp landade på dess hörn. Utan etikett är ikonen centrerad,
 * och samma tal hamnade en bit ovanför den. Nu räknas läget från mitten:
 * ikonen är 1,5 rem, alltså .75 rem upp till dess överkant, och brickan
 * lägger sig med sin nederkant strax innanför den. */
.tab__count { top:50%; left:50%; margin-left:0;
  transform:translate(.1875rem,-.9375rem);
  border:2px solid var(--background) }

/* En prick utan siffra. Hemsidan har inget att räkna — antingen ligger
   något och väntar på att publiceras eller inte — och en etta hade
   antytt att det gick att beta av dem en och en. */
.tab__count--dot { min-width:.625rem; height:.625rem; padding:0;
  transform:translate(.375rem,-.8125rem) }
.bar__btn { position:relative }
.bar__count { top:.4375rem; right:.4375rem; border:2px solid var(--background) }

/* Uppgiften är själv sin länk, men utan understrykning: åtgärderna står
   utskrivna som knappar längst ned, så adressen behöver inte bära
   affordansen — den behöver bara vara läsbar. Den är fortfarande markerbar
   som text, vilket en knapp inte är. */
.kv__link { display:block; color:inherit; text-decoration:none }
.kv__link:active { color:var(--accent) }

/* Det långa fältet. Samma kolumn och samma etikett som de korta — bara
   radbrytningarna bevarade och lite mer luft mellan raderna att läsa i. */
.kv__val--long { white-space:pre-wrap; font-size:.9375rem; line-height:1.65 }

/* ---------- referensbilder för bildmanér ----------
   Små rutor i rad. De ska gå att känna igen, inte granskas — den som vill
   se en bild i detalj har den redan på sin egen skärm. */
.styleRefs { display:flex; flex-wrap:wrap; gap:.5rem; margin-bottom:.75rem }
.styleRef { position:relative; width:4.5rem; height:4.5rem; border-radius:.625rem;
  overflow:hidden; background:var(--surface-2) }
.styleRef img { width:100%; height:100%; object-fit:cover; max-width:none; display:block }
.styleRef__x { position:absolute; top:.1875rem; right:.1875rem;
  width:1.25rem; height:1.25rem; border:0; border-radius:50%; cursor:pointer;
  background:rgba(var(--ink-rgb),.62); color:#fff; font-size:.875rem; line-height:1;
  display:flex; align-items:center; justify-content:center; padding:0 }

/* Papperskorgen i minneslistan. Diskret tills man letar efter den — den
   som läser minnet gör det oftast för att kontrollera, inte för att rensa. */

/* Sökfältet i listorna. Lägre än ett vanligt fält — det är ett filter man
   passerar, inte något man fyller i. Krysset som iOS lägger in behålls;
   det är den snabbaste vägen tillbaka till hela listan. */
/* Luppen står till vänster och texten efter den. Symbolen säger vad
   fältet är, och det ska den säga innan man börjar läsa raden — inte
   efter. Krysset behåller högerkanten: det hör till det man skrivit och
   dyker upp först när det finns något att rensa. */
/* Sökfältet står på sidans egen botten och inte på en kortfärgad platta.

   Fältet är en kontroll och inte innehåll: en ifylld ruta bredvid en
   lista med kortfärgade rader läste sig som ännu en rad, en som råkade
   ligga ovanför rubriken. Med samma botten som sidan är det ramen och
   luppen som säger vad det är, vilket räcker.

   background-color och inte background: kortformen slår ut luppen, som
   ligger som en bakgrundsbild här nedan. Fältet stod utan sin ikon och
   ingen kunde se varför. */
.ctrl--search { background-color:transparent;
  padding:.6875rem 1rem .6875rem 2.75rem; border-radius:var(--radius-pill);
  background-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22rgba%2817%2C17%2C17%2C1%29%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%3E%3Ccircle%20cx%3D%2211%22%20cy%3D%2211%22%20r%3D%227%22%2F%3E%3Cline%20x1%3D%2216.5%22%20y1%3D%2216.5%22%20x2%3D%2221%22%20y2%3D%2221%22%2F%3E%3C%2Fsvg%3E");
  background-repeat:no-repeat; background-position:left 1rem center;
  background-size:1.0625rem 1.0625rem }

/* Luppen i mörkt läge. Strecket ligger inbakat i bildens data-adress och
   kan inte läsa en variabel — samma bild igen med ljust bläck, i temats
   båda block (se mörkt läge överst: en mediefråga och en attributväljare
   kan inte dela regel). Utan detta stod en mörk lupp i ett mörkt fält.

   Full styrka och inte dämpad. Luppen säger vad rutan är; platshållaren
   säger vad man kan skriva i den. Det första ska synas direkt, det andra
   ska stå tillbaka — de har olika uppgift och ska inte väga lika. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .ctrl--search {
    background-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22rgba%28233%2C232%2C228%2C1%29%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%3E%3Ccircle%20cx%3D%2211%22%20cy%3D%2211%22%20r%3D%227%22%2F%3E%3Cline%20x1%3D%2216.5%22%20y1%3D%2216.5%22%20x2%3D%2221%22%20y2%3D%2221%22%2F%3E%3C%2Fsvg%3E");
  }
}
:root[data-theme="dark"] .ctrl--search {
  background-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22rgba%28233%2C232%2C228%2C1%29%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%3E%3Ccircle%20cx%3D%2211%22%20cy%3D%2211%22%20r%3D%227%22%2F%3E%3Cline%20x1%3D%2216.5%22%20y1%3D%2216.5%22%20x2%3D%2221%22%20y2%3D%2221%22%2F%3E%3C%2Fsvg%3E");
}

/* Luppen låg förut kvar bara så länge fältet var tomt, för att inte hamna
   under krysset. Krysset sitter i högerkanten och luppen i vänstra — de
   möts aldrig, och regeln lämnade bara ett tomt hål i vänsterkanten så
   fort man skrev en bokstav. Fältet slutade se ut som ett sökfält i samma
   stund man började söka. */

/* Webbläsarens eget kryss bort. Det syns bara vid fokus, är ett par pixlar
   stort och går inte att färgsätta — vi ritar ett eget med en yta man
   träffar med tummen. */
.ctrl--search::-webkit-search-cancel-button { -webkit-appearance:none; display:none }

.search { position:relative }
.search__x { position:absolute; top:50%; right:.375rem; transform:translateY(-50%);
  display:grid; place-items:center; width:2rem; height:2rem; padding:0;
  border:0; border-radius:50%; background:none; color:rgba(var(--ink-rgb),.5);
  cursor:pointer }
.search__x svg { width:.9375rem; height:.9375rem; stroke:currentColor; fill:none;
  stroke-width:2; stroke-linecap:round }
.search__x:active { background:var(--surface-2); color:var(--foreground) }

/* Rullgardinen i redigeraren. Egen pil, för webbläsarens egen sitter olika
   på varje plattform och drar blicken mer än den behöver.

   Färgen är hårdkodad i bildens data-adress — en SVG i en url() är ett
   eget dokument och kan inte läsa sidans variabler, så var(--ink-rgb)
   där var ogiltigt bläck och pilen ritades aldrig, i något tema. Samma
   lösning som luppen i .ctrl--search: ljust bläck i temats båda mörka
   block nedanför. */
.ctrl--select { appearance:none; -webkit-appearance:none; padding-right:2.5rem;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='rgba(17,17,17,0.45)' stroke-width='2' stroke-linecap='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 1rem center;
  background-size:1rem; cursor:pointer }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .ctrl--select {
    background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='rgba(233,232,228,0.45)' stroke-width='2' stroke-linecap='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  }
}
:root[data-theme="dark"] .ctrl--select {
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='rgba(233,232,228,0.45)' stroke-width='2' stroke-linecap='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
}


/* ==================== GÄSTLAYOUTEN PÅ SKRIVBORD ====================
   Välkomst- och inloggningsvyerna är byggda för en smal spalt. Mellan 760
   och 1100 px ritas de i telefonramen ovanför, vilket är rätt: en app som
   sträcks ut över en hel skärm ger fel känsla för radlängder och för hur
   långt ned en knapp hamnar.

   Över 1100 px räcker inte ramen. En tavla mitt i ett tomt fönster är inte
   en produkt någon litar på, och skärmen har då plats för det som faktiskt
   behöver sägas på en dator: att appen hör hemma i telefonen, och hur den
   kommer dit.

   Layouten är analytics-inloggningens: formuläret till vänster mot ljust,
   en mörk sida till höger. Samma två spalter, samma varp, samma glöd.

   Elementen finns i märkspråket hela tiden och ritas bara här. Att bygga
   och riva dem vid varje ändring av fönstret vore mer kod och en blinkning
   varje gång någon drar i fönsterkanten. */
.guestBrand, .guestSide { display:none }

@media (min-width:1100px) {
  /* Ramen lämnas. Allt som gjorde #app till en telefon nollställs.

     Marken sätts här och inte av temat: gästvyerna är byggda för mörkt och
     bär vit text, så en ljus botten i ljust tema vore text på sin egen färg.
     En variabel i stället för värdet på två ställen — spalten till vänster
     läser samma rad, och då kan de inte glida isär. */
  body:not(.is-app) { display:block; min-height:0; padding:0;
    --gastmark:#000; background:var(--gastmark) }

  body:not(.is-app) #app {
    display:block; position:relative;
    width:100%; height:100dvh;
    aspect-ratio:auto; border-radius:0; box-shadow:none;
    overflow:hidden;
    --top:0px;                     /* ingen statusrad att hålla undan för */
    /* Vänsterkanten. Varumärket i hörnet och innehållet under det står på
       samma linje — två marginaler som råkar likna varandra glider isär
       första gången någon ändrar den ena. */
    --gutter:clamp(3rem, 6vw, 5.5rem);
  }

  /* Skärmarna tar vänstra halvan. De är position:fixed inuti #app, som
     har transform och därmed är deras containing block — så en högerkant
     räcker för att flytta in dem. */
  body:not(.is-app) .screen {
    right:50%;
    border-radius:0;
    /* Samma botten som marken runt .guestSide, inte panelens egen. Då
       finns ingen söm där spalten möter ramen — de flyter ihop, och
       panelen är det enda som läser som ett eget skikt. Genomskinlig
       duger inte: skärmarna glider över varandra och skulle synas genom. */
    background:var(--gastmark);
    /* Inglidningen sker fortfarande, men över en halv skärm i stället för
       en hel. translate3d(100%) är relativt elementets egen bredd och
       följer med av sig självt. */
  }

  /* Varpen och glöden hör till den mörka panelen. Väljaren är avsiktligt
     smal: .guestSide har egna med samma klassnamn och ska behålla dem. */
  body:not(.is-app) .screen > .warp,
  body:not(.is-app) .screen > .glow { display:none }

  /* ---- vänstra spalten ----
     Skärmarna är byggda för mörkt (.ink) och bär .onInk på sina fält.
     De behåller sin egen botten här: var(--ink), samma som .guestSide,
     så de två halvorna möts i en och samma färg. */

  /* Topplisten hör till telefonen. Rubriken i den upprepar den som står i
     formuläret, och bakåtpilen sitter i skärmens hörn där varumärket ska
     stå. Båda uppgifterna tas över: rubriken av formuläret, vägen tillbaka
     av märket. */
  body:not(.is-app) .ink .bar { display:none }

  /* Innehållet mittas lodrätt och får luft, i stället för att tryckas mot
     underkanten som det gör i en telefon. */
  body:not(.is-app) .hero {
    justify-content:center;
    padding:0 var(--gutter);
    max-width:38rem;
  }
  body:not(.is-app) .hero__top { display:none }   /* varumärket står i hörnet */
  body:not(.is-app) .hero__mid { margin-top:0; padding:0 }
  body:not(.is-app) .hero__mid h1 { font-size:clamp(2.5rem, 3.4vw, 3.25rem) }
  body:not(.is-app) .hero__mid p { font-size:1rem; max-width:34ch }
  body:not(.is-app) .hero .stack { margin-top:2.5rem; max-width:22rem }

  body:not(.is-app) .authForm {
    justify-content:center;
    padding:0 var(--gutter) 2rem;
    max-width:38rem;
  }
  body:not(.is-app) .authForm__head h1 { font-size:clamp(2rem, 2.6vw, 2.5rem) }
  body:not(.is-app) .authForm__foot { margin-top:1.75rem; padding-top:0 }

  /* ---- varumärket i hörnet ----
     Ett element för alla gästvyer i stället för ett per skärm: det står
     stilla när vyerna glider förbi, vilket är vad ett varumärke ska göra. */
  body:not(.is-app) .guestBrand {
    display:block; position:fixed; z-index:40;
    top:2.75rem; left:var(--gutter);
    padding:0; border:0; background:none; cursor:pointer;
    border-radius:.25rem;
  }
  body:not(.is-app) .guestBrand img { height:1.375rem; width:auto; display:block }

  /* ---- den mörka sidan ---- */
  body:not(.is-app) .guestSide {
    display:flex; flex-direction:column; justify-content:flex-end;
    position:fixed; z-index:15;
    top:1rem; right:1rem; bottom:1rem; left:50%;
    overflow:hidden; isolation:isolate;
    background:var(--ink); color:#fff;
    border-radius:var(--radius-card);
  }
  .guestSide__inner { position:relative; z-index:1; padding:clamp(2.5rem, 4vw, 3.5rem) }

  .guestSide__lead {
    font-size:clamp(1.25rem, 1.7vw, 1.625rem); font-weight:500;
    line-height:1.4; letter-spacing:-.015em; color:#fff;
  }

  /* Kortet med koden. Ljust mot det mörka: en QR-kod behöver kontrast åt
     rätt håll för att gå att läsa, och en ljus kod på mörk botten är den
     vanligaste orsaken till en kod som inte fungerar. */
  .guestSide__card {
    margin-top:2.5rem; display:flex; align-items:center; gap:1.25rem;
    padding:1.125rem; max-width:26rem;
    background:rgba(255,255,255,.06);
    border:1px solid rgba(255,255,255,.12);
    border-radius:1.25rem;
    backdrop-filter:blur(6px);
  }
  .guestSide__qr {
    flex:0 0 auto; width:6.5rem; height:6.5rem; padding:.5rem;
    background:#fff; border-radius:.75rem; color:var(--ink);
  }
  .guestSide__qr .qr { display:block; width:100%; height:100% }
  .guestSide__text { display:flex; flex-direction:column; gap:.375rem }
  .guestSide__text strong {
    font-size:.9375rem; font-weight:600; letter-spacing:-.01em; color:#fff;
  }
  .guestSide__text span { font-size:.8125rem; line-height:1.55; color:rgba(255,255,255,.55) }

  /* Dialogen är en bottenlåda på telefon. Utsträckt över en hel skärm blir
     den en remsa längst ned som ser ut som en kakruta, och det är inte vad
     den är. Samma behandling som i inloggat läge: en dialog mitt på
     skärmen, där blicken redan är. Skillnaden är att här finns ingen meny
     till vänster att räkna bort. */
  body:not(.is-app) .sheet__panel {
    left:50%; right:auto; bottom:auto; top:50%;
    width:min(24rem, calc(100vw - 3rem));
    border-radius:1.25rem;
    padding:1.5rem 1.25rem;
    transform:translate3d(-50%,-46%,0); opacity:0;
    transition:transform .22s cubic-bezier(.25,.46,.45,.94), opacity .22s;
  }
  body:not(.is-app) .sheet__scrim { position:fixed; inset:0 }
  body:not(.is-app) .sheet.is-in .sheet__panel { transform:translate3d(-50%,-50%,0); opacity:1 }
  body:not(.is-app) .sheet__grip { display:none }
}

/* Två länkar under en knapp, med en prick emellan. Staplade hade de sett
   ut som två knappar till, och den som söker "glömt lösenordet" hade fått
   läsa båda för att hitta den. */
.authLinks { display:flex; align-items:center; justify-content:center; gap:.5rem; flex-wrap:wrap }
.authLinks .btn--plain { width:auto; flex:0 0 auto; min-height:2.25rem; padding:0 }


/* ==================== LÄGG TILL PÅ HEMSKÄRMEN ====================
   Rutan som visas en gång, och som länken under Logga in tar fram igen.
   Stegen är numrerade därför att ordningen spelar roll: den som trycker
   Lägg till innan hen tryckt Dela hittar ingenting. */
.installSteps { display:flex; flex-direction:column; gap:1rem; counter-reset:steg }
.installStep {
  /* 1,5 rem och inte .875. Siffran ligger på ikonens högra kant och
     skjuter 14 px utanför den — precis lika mycket som den gamla luckan
     var bred, så brickan tog hela mellanrummet och nuddade texten. Med
     24 px blir det tio pixlar luft kvar på andra sidan brickan. */
  display:flex; align-items:flex-start; gap:1.5rem;
  counter-increment:steg;
}
/* Siffran före ikonen. Två spår att följa är ett för mycket, så numret
   ligger i ikonens ruta i stället för bredvid den. */
/* Ikonen är en kontur, siffran är fylld.

   Båda bar accenten förut, och då tävlade de om samma blick: en orange
   cirkel med en orange bricka på. Nu bär ringen bara en kant och siffran
   färgen — ordningsnumret är det som säger var man är, och symbolen det
   som säger vad man ska trycka på. */
.installStep__ico {
  position:relative; flex:0 0 auto;
  width:3rem; height:3rem; border-radius:50%;
  display:grid; place-items:center;
  background:var(--background); color:var(--foreground);
  border:1px solid var(--line);
}
.installStep__ico svg {
  width:1.5rem; height:1.5rem;
  stroke:currentColor; fill:none; stroke-width:1.75;
  stroke-linecap:round; stroke-linejoin:round;
}
/* Siffran på ikonens vänstra kant, i höjd med mitten.

   Den satt i övre vänstra hörnet och läste sig som en bricka klistrad på
   något — två föremål. På mittlinjen och med en liten överlappning läses
   den i stället som en del av samma sak: numret först, ikonen sedan, i
   den ordning man läser raden.

   Ringen i grindens bakgrundsfärg klipper ut cirkeln ur ikonen, så
   överlappningen blir tydlig i stället för grumlig. */
.installStep__ico::after {
  content:counter(steg);
  /* Höger kant, och -1rem in. Cirkeln är 48 px och tecknet i den 24,
     alltså från 12 till 36. En bricka på 22 px som slutar 16 px utanför
     börjar vid 42 — sex pixlar in på cirkeln, långt från symbolen. */
  position:absolute; top:50%; right:-1rem;
  transform:translateY(-50%);
  width:1.375rem; height:1.375rem; border-radius:50%;
  display:grid; place-items:center;
  background:var(--accent); color:#fff;
  box-shadow:0 0 0 .125rem var(--background);
  font-size:.6875rem; font-weight:600; line-height:1;
  font-variant-numeric:tabular-nums;
}
.installStep__text {
  /* Första textraden i höjd med siffran.

     Siffran sitter på ikonens mittlinje, alltså 24 px ned i en cirkel på
     48. Texten ska börja där och inte i cirkelns överkant — annars läses
     numret och raden det hör till som två saker på olika höjd.

     Uträknat och inte gissat: halva ikonen minus halva radhöjden.
     Radhöjden är .9375rem × 1,5 = 22,5 px, så stoppningen blir 12,75 px.
     flex-start och inte center: växer texten till flera rader ska den
     fortsätta nedåt från siffran, inte skjuta den ur led. */
  padding-top:calc((3rem - .9375rem * 1.5) / 2);
  font-size:.9375rem; line-height:1.5; color:var(--foreground);
}
.installStep__text b { font-weight:600 }

/* Koden i rutan, för den som sitter vid en dator utan sidopanel. */
.installQr {
  width:11rem; height:11rem; margin:0 auto; padding:.875rem;
  background:#fff; color:var(--ink);
  border:1px solid var(--line); border-radius:var(--radius-control);
}
.installQr .qr { display:block; width:100%; height:100% }

/* En avbrytknapp utan text är ingen knapp. Rutor som bara ska stängas har
   en enda knapp, och då ska den stå ensam. */
#sheet-cancel:empty { display:none }


/* ==================== INSTALLATIONSGRINDEN ====================
   Appen vägrar köra i en flik på telefon. Skärmen är appens egen, mörk och
   med samma varp och glöd som välkomstvyn — det här är inte ett felmeddelande
   utan första mötet, och det ska se ut som produkten.

   Ingen stängknapp, ingen väg förbi i gränssnittet. Det är hela poängen.

   Hette .gate förut, vilket sektionsredigerarens växel också gjorde. Den
   här står sist i filen och vann därför striden: en växel i en sektion
   blev en svart heltäckande yta i lådan i stället för ett kort. Namnet
   säger nu vilken grind det är. */
.installGate {
  /* Grinden ligger utanför #app — den ska stå kvar även om skalet inuti
     aldrig ritas — och mäter alltså mot iOS trasiga underkant. Den får
     bristen tillagd direkt i stället. */
  position:fixed; inset:0; z-index:500;
  display:flex; flex-direction:column;
  overflow-y:auto;
  background:var(--ink); color:#fff;
}
.installGate[hidden] { display:none }

/* Samma uppställning som välkomstskärmen: märket uppe, innehållet mot
   underkanten där tummen är. Utan flex:1 och auto-marginalen nedanför
   flyter logotypen med texten och hamnar olika högt beroende på hur många
   steg plattformen råkar kräva. */
/* Innehållet i mitten, frågan kvar i botten.

   Rubriken hade en logotyp ovanför sig och ett tomrum emellan som växte
   eller krympte med antalet steg — skärmen såg olika ut på olika iOS utan
   att något annat skilde dem åt. Nu bär mitten allt som hör till
   uppgiften och centreras i den plats som blir över, medan raden längst
   ned står still: den handlar om undantaget och ska inte flytta sig när
   stegen byts. */
.installGate__inner {
  position:relative; z-index:1;
  display:flex; flex-direction:column; flex:1 1 auto;
  padding:calc(2rem + env(safe-area-inset-top)) 1.5rem
          calc(2rem + env(safe-area-inset-bottom));
}
.installGate__mid {
  flex:1 1 auto; display:flex; flex-direction:column; justify-content:center;
  min-height:0;
}
.installGate__foot { flex:0 0 auto; margin-top:2rem }

/* Märket uppe i hörnet, som förut. Det hör till skärmen och inte till
   meningen — rubriken säger vad man ska göra, och en logotyp inbakad i
   den gjorde handlingen till en produktrubrik. */
.installGate__logo { width:9.5rem; height:auto; flex:0 0 auto }

.installGate__title {
  margin-top:.5rem;
  font-size:2rem; font-weight:600; letter-spacing:-.035em; line-height:1.1;
}
/* Texten står ovanför rubriken nu, så ingen toppmarginal. */
.installGate__text {
  margin:0; max-width:32ch;
  font-size:.9375rem; line-height:1.65; color:rgba(255,255,255,.6);
}
.installGate__body { margin-top:2.25rem }
.installGate__btn  { margin-top:.5rem }

/* Stegen står mot mörkt här, till skillnad från i dialogen. Samma
   uppställning, andra färger. */
/* I grinden är botten svart oavsett tema, så kanten och urklippet måste
   sättas mot den och inte mot appens bakgrundsfärg. */
/* Solid botten, inte genomskinlig.

   Grinden har en randig hinna (.warp) och en glöd i nederkanten över sin
   svarta botten. En genomskinlig cirkel släppte igenom ränderna, så
   ikonen såg ut att ligga bakom ett galler i stället för ovanpå. En egen
   färg täcker dem — samma svarta som grunden, så bara kanten ritar
   cirkeln. */
.installGate .installStep__ico {
  background:#0a0a0a; color:#fff;
  border-color:rgba(255,255,255,.22);
}
/* Samma sak för frågans lilla bricka. Den låg på en genomskinlig vit och
   fick ränderna genom sig — här en solid grå, ljus nog att läsas som en
   bricka mot den svarta. */
.installGate .askIco {
  background:#0a0a0a; color:#fff;
  border-color:rgba(255,255,255,.22);
}

/* Frågans ikon byter med stegen. Samma kryssruta som växlar
   uppsättningarna, så de aldrig kan säga olika saker. */
.installGate .installStep__ico::after {
  background:var(--accent); color:#fff;
  box-shadow:0 0 0 .125rem var(--ink);
}
.installGate .installStep__text { color:rgba(255,255,255,.82) }
.installGate .installStep__text b { color:#fff }

/* Reservfältet som markerar adressen när urklippsapi:t saknas. */
.installGate__body input {
  margin-top:.75rem; width:100%;
  font:inherit; font-size:.875rem; color:#fff;
  background:rgba(255,255,255,.06);
  border:1px solid rgba(255,255,255,.16);
  border-radius:var(--radius-control); padding:.75rem 1rem;
}

/* På en större skärm mittas innehållet i stället för att ligga mot
   underkanten. Grinden gäller inte skrivbordet, men en surfplatta i
   liggande läge är bred nog att se tom ut utan det här. */
@media (min-width:34rem) {
  .installGate__inner { max-width:32rem; margin-inline:auto; width:100% }
}

/* Raden under stegen. Den finns för att listan inte kan bli fullständig:
   Safaris verktygsrad går att ändra i inställningarna, och Apple flyttar
   den mellan versioner. Tonad, för den gäller undantaget. */
.installGate .installHint { color:rgba(255,255,255,.5) }
.installGate .installHint b { color:rgba(255,255,255,.8) }

/* Växeln mellan de två Safari-layouterna.

   Versionen ur user agent-strängen avgör vilken uppsättning som ligger
   överst, men den kan ha frusit och layouten går dessutom att ändra i
   Safaris inställningar. Då ska det gå att byta — och byta till en hel
   uppsättning steg, inte till en mening om att knappen kanske sitter
   någon annanstans.

   Kryssruta och inte knapp: grinden ritas innan appens händelsebindning
   finns, och en växel som inte fungerar i det ögonblick den syns är
   sämre än ingen växel alls. */
/* Växeln styrs från grinden, inte från stegrutan.

   Kryssrutan flyttade ned i fotraden när frågan blev ett eget block, och
   den ligger alltså inte längre INUTI .installSwap. :has() letade i fel
   träd och matchade aldrig: stegen bytte inte, och knappen såg död ut.
   Villkoret ställs där båda finns — på grinden. */
.installSwap > .installSteps--alt { display:none }
.installGate:has(.installSwap__box:checked) .installSteps--main { display:none }
.installGate:has(.installSwap__box:checked) .installSteps--alt  { display:flex }

/* Frågan längst ned, byggd som ett steg.

   Den handlar om samma knapp som stegen ovanför, och en knapp som ritas
   på två sätt på samma skärm läses som två knappar. Samma cirkel, samma
   storlek — frågan i full styrka och svaret under den, understruket, för
   det är svaret man trycker på. */
.installAsk {
  display:block;
  padding-top:1.25rem; cursor:pointer;
  border-top:1px solid rgba(255,255,255,.1);
}
/* Plats åt pekaren — på texten, inte på hela fotraden.

   "Tryck här" ligger fast i nedre högra hörnet och svarstexten löpte rakt
   in under den. Först la jag stoppningen på fotraden, och då kortades
   avdelaren ovanför lika mycket: en linje som slutar en bit in ser ut som
   ett fel. Texten väjer, linjen går hela vägen.

   Bara medan pekaren är framme. Utan pil finns ingenting att väja för. */
/* Frågeraden ligger i botten, pilen står kvar i sitt hörn.

   Ingen extra bottenstoppning alltså — raden går ända ned. De delar
   därmed samma band i höjdled, och väjningen sker i sidled i stället.

   Stoppningen håller undan en LÅNG rad från pilen till höger. Den kan
   inte knuffa en kort rad åt vänster — stoppning begränsar bara var texten
   bryter, och "Ser du inte •••?" är 116 px och bryter aldrig. */
.installGate--point .installAsk__text { padding-right:7rem }

/* Smala skärmar: pilen tappar sitt ord.

   Med pilen i mitten står den ovanför radens text, och uppmätt håller det
   ned till ungefär 366 px bredd — vid 390 är luften 14 px, vid 320 är den
   minus 21. Under den gränsen är pillret bredare än vad som får plats
   bredvid frågan, och då får pilen tala för sig själv: en pil som pekar
   nedåt behöver inte stå "tryck här" för att förstås, och den lilla
   skärmen har inte plats för båda. */
@media (max-width:374px) {
  .installGate--point:not(.installGate--dots) .installPoint__say,
  .installGate--point.installGate--dots:has(.installSwap__box:checked) .installPoint__say {
    display:none;
  }
}
/* Ingen siffra på frågans ikon. Cirkeln lånas från stegen för att se
   likadan ut, och stegens ::after bär ordningsnumret ur en räknare — utan
   ett <li> att räkna landade den på noll. Frågan är inget steg och ska
   inte numreras. */
.installAsk .installStep__ico::after { display:none }
.installAsk__text { display:flex; flex-direction:column; gap:.1875rem; min-width:0 }
.installAsk__q {
  font-size:.9375rem; line-height:1.5; color:rgba(255,255,255,.82);
}
.installAsk__q b { color:#fff; font-weight:600 }
/* Knappen ritad mitt i meningen. Samma cirkel som stegens, nedskalad till
   textens grad — inte en ikon bredvid texten utan ett ord i den.

   inline-flex och marginaler, inte en flexrad med gap: frågetecknet ska
   sitta tätt efter ikonen som efter ett ord, och en gap hade lagt luft
   även där. Med "Ser du inte ⬤?" blir mellanrummen ett före och inget
   efter, precis som runt vilket ord som helst. */
.askIco {
  display:inline-flex; align-items:center; justify-content:center;
  vertical-align:middle; margin:0 .0625rem 0 .3125rem;
  width:1.625rem; height:1.625rem; border-radius:50%;
  /* Samma klädsel som stegens cirklar, nedskalad: solid botten och en
     tunn kant. Neutral och inte accent — stegen ovanför bär accenten
     därför att de är det man ska göra; frågan är en reserv för den som
     inte hittar rätt, och en till orange cirkel längst ned hade dragit
     blicken dit i stället. */
  background:var(--background); color:var(--foreground);
  border:1px solid var(--line);
}

.askIco svg {
  width:.875rem; height:.875rem;
  stroke:currentColor; fill:none; stroke-width:1.75;
  stroke-linecap:round; stroke-linejoin:round;
}

/* Frågans ikon byter med stegen, av samma kryssruta — de kan aldrig säga
   olika saker.

   Reglerna står EFTER .askIco och inte före. De har samma specificitet
   som basregeln, så den som kommer sist vinner: låg de före visades båda
   ikonerna samtidigt, eftersom basens display:inline-flex då skrev över
   display:none. */
.askIco--alt { display:none }
.installGate:has(.installSwap__box:checked) .askIco--main { display:none }
.installGate:has(.installSwap__box:checked) .askIco--alt  { display:inline-flex }
.installAsk__a {
  font-size:.875rem; line-height:1.5; color:#fff;
  text-decoration:underline; text-underline-offset:.25em;
  text-decoration-color:rgba(255,255,255,.35);
}

/* Pekaren mot delningsknappen.

   Grinden kan inte trycka åt någon, och den kan inte peka på något
   utanför sidan — Safaris verktygsrad ligger utanför dokumentet. Det
   närmaste är att stå precis ovanför den, i samma hörn, och peka nedåt.
   Ritas bara när stegen faktiskt handlar om nedre högra hörnet. */
/* Pekaren flyttar dit knappen faktiskt sitter.

   Två lägen, för Safari har två. Med ••• ligger knappen längst till höger
   i raden; med delningsikonen står den i mitten, som tredje av fem. Pilen
   följer den uppsättning steg som visas — byter man, byter den plats.

   Rörelsen ligger på barnen och inte på rutan: centreringen använder
   transform, och en animering på samma egenskap hade slagit ut den. */
.installPoint { display:none }
.installGate--point .installPoint {
  display:flex; flex-direction:column; align-items:center; gap:.25rem;
  position:fixed; z-index:2;
  bottom:calc(.75rem + env(safe-area-inset-bottom));
  pointer-events:none;
}
.installGate--point .installPoint > * {
  animation:pointBob 1.6s ease-in-out infinite;
}

/* Höger: när stegen som visas handlar om ••• */
.installGate--point.installGate--dots .installPoint,
.installGate--point:not(.installGate--dots):has(.installSwap__box:checked) .installPoint {
  right:1.25rem; left:auto; transform:none;
}

/* Mitten: när stegen som visas handlar om delningsikonen */
.installGate--point:not(.installGate--dots) .installPoint,
.installGate--point.installGate--dots:has(.installSwap__box:checked) .installPoint {
  left:50%; right:auto; transform:translateX(-50%);
}
.installPoint__say {
  padding:.375rem .75rem; border-radius:999px;
  background:#fff; color:var(--ink);
  font-size:.8125rem; font-weight:600; white-space:nowrap;
  box-shadow:0 .5rem 1.5rem rgba(0,0,0,.45);
}
.installPoint__arrow {
  width:1.25rem; height:2rem;
  stroke:#fff; fill:none; stroke-width:2.5;
  stroke-linecap:round; stroke-linejoin:round;
  filter:drop-shadow(0 .25rem .5rem rgba(0,0,0,.5));
}
@keyframes pointBob {
  0%, 100% { transform:translateY(0) }
  50%      { transform:translateY(.375rem) }
}
@media (prefers-reduced-motion:reduce) {
  .installGate--point .installPoint > * { animation:none }
}

/* Kryssrutan är växeln. Osynlig men kvar i märkspråket: den bär läget,
   och etiketten runt den är det man trycker på. Ingen lyssnare — grinden
   ritas innan appens händelsebindning finns och måste fungera i det
   första ögonblicket den syns. */
.installSwap__box { position:absolute; opacity:0; pointer-events:none }

/* ==================== SOCIALA MEDIER ====================
   Fliken där inläggen planeras, och redigeraren där de görs. */

/* Märket för ett inlägg som inte gick ut. De andra märkena finns redan;
   det här saknades, för ingen annan lista i appen har ett läge som kräver
   en åtgärd av den som ser det. */
.badge--fail { background:rgba(179,38,30,.12); color:var(--danger) }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .badge--fail { background:var(--danger); color:#fff }
}
:root[data-theme="dark"] .badge--fail { background:var(--danger); color:#fff }

/* Felet i klartext under raden. Det står på raden och inte bakom ett
   tryck: ett inlägg som inte gick ut är inget man ska behöva öppna för
   att förstå. */
.prow__err {
  display:block; margin-top:.375rem; font-size:.8125rem; line-height:1.45;
  color:var(--danger);
}

/* ---- redigeraren ---- */

.soPost { padding-top:.5rem }

/* Duken står i en ruta med fast kvot. Utan den hoppar hela sidan när
   bilden laddas, och man tappar det man höll på med. */
.soStage {
  position:relative;
  width:100%; max-width:26rem; margin:0 auto 1.25rem;
  background:var(--surface-2); border-radius:var(--radius-card-sm, 1.25rem);
  overflow:hidden; box-shadow:0 1px 2px rgba(17,17,17,.04), 0 14px 34px -18px rgba(17,17,17,.26);
}
.soStage canvas {
  display:block; width:100%; height:100%;
  /* Fingret ska dra i lagret, inte rulla sidan. */
  touch-action:none; cursor:grab;
}
.soStage canvas:active { cursor:grabbing }

/* Bilderna i inlägget, som en remsa under duken. Rutorna är små och
   fyrkantiga oavsett format: de är knappar att byta med, inte
   förhandsvisningar. Den aktiva bär accentringen; plusset är en tom
   ruta med streckad kant, så det läses som platsen för nästa bild. */
.soSlides {
  display:flex; flex-wrap:wrap; align-items:center; gap:.5rem;
  max-width:26rem; margin:-.5rem auto 1.25rem;
}
.soSlide {
  position:relative; flex:0 0 auto; width:3rem; height:3rem;
  border-radius:.625rem; overflow:hidden; padding:0; border:0;
  background:var(--surface-2); cursor:pointer;
  box-shadow:inset 0 0 0 1px rgba(var(--ink-rgb),.12);
}
.soSlide img { width:100%; height:100%; object-fit:cover; display:block }
.soSlide__tom { display:block; width:100%; height:100%; background:rgba(var(--ink-rgb),.06) }
.soSlide__n {
  position:absolute; left:.25rem; bottom:.25rem;
  min-width:1.125rem; height:1.125rem; padding:0 .25rem; border-radius:999px;
  background:rgba(10,10,10,.65); color:#fff;
  font-size:.625rem; font-weight:600; line-height:1.125rem; text-align:center;
}
.soSlide.is-on { box-shadow:inset 0 0 0 2px var(--accent) }
.soSlide--add {
  display:grid; place-items:center; background:transparent;
  box-shadow:none; border:1px dashed rgba(var(--ink-rgb),.35);
  color:rgba(var(--ink-rgb),.6);
}
.soSlide--add svg { width:1.125rem; height:1.125rem; stroke:currentColor; fill:none;
  stroke-width:2; stroke-linecap:round }
.soSlides__del { margin-left:auto; min-height:2.25rem; padding:.25rem .5rem }
.soSlides__hint { flex:1 1 100%; margin:0; line-height:1.45 }

/* Lagerraden. Piller och inte en lista: de är fyra, de byts ofta, och de
   ska ligga inom räckhåll för tummen precis under duken. */
/* Lagren är flikar på reglagekortet — samma seg--card som listflikarna,
   och kortet under möter dem på samma sätt som listkortet gör: utan
   överhörn och utan egen överkant där flikraden har sin platta. Under
   1100 px tappar raden plattan (se .seg--card i mediefrågan) och kortet
   får sina hörn tillbaka av sig självt. */
.soLayers { margin-top:1.25rem }
@media (min-width:1100px) {
  .soLayers + .card { border-top-left-radius:0; border-top-right-radius:0; border-top:0 }
}
/* Ett släckt lager i flikraden: kvar att trycka på, men märkt. */
.soLayers button.is-off { opacity:.55; text-decoration:line-through }

/* Färgvalet. Rutor och inte namn: färgen ÄR etiketten, och tre ord som
   beskriver tre färger säger mindre än de tre färgerna. */
.soColor {
  width:1.875rem; height:1.875rem; border-radius:.5rem; cursor:pointer;
  border:1px solid var(--line);
  box-shadow:inset 0 0 0 0 var(--accent); transition:box-shadow .16s;
}
.soColor.is-on { box-shadow:0 0 0 2px var(--background), 0 0 0 4px var(--accent) }
/* Tätt ihop: ett val mellan tre, inte tre inställningar bredvid varandra.
   Rutorna låg i en rowSplit med .5rem emellan och lästes då som tre
   skilda reglage. Gapet rymmer fortfarande markeringsringen. */
.soColors { display:flex; flex-wrap:wrap; gap:.375rem }

/* Formatvalet står ovanför duken och inte under den: det avgör dukens
   form, och en duk som byter proportioner efter att man komponerat i den
   flyttar allt man just ställt in. */
.soFormat { margin:.25rem 0 1rem }

/* Skrivfältet ovanpå duken.
   Texten är genomskinlig med flit — duken ritar bokstäverna, fältet bidrar
   med markör, markering och tangentbord. Två synliga renderingar av samma
   text hade betytt två radbrytningar, och den som gäller är den som
   exporteras.
   pointer-events är av tills fältet har fokus: annars fångar det
   dragningen, och lagret går inte längre att flytta med fingret. Tab når
   det ändå, för tangentbordsfokus bryr sig inte om pointer-events. */
.soEdit {
  position:absolute; z-index:2; margin:0; padding:0; border:0; resize:none;
  overflow:hidden; background:none; outline:none; pointer-events:none;
  color:transparent; -webkit-text-fill-color:transparent;
  letter-spacing:inherit; text-align:left;
}
.soEdit:focus { pointer-events:auto }
.soEdit::selection { background:rgba(177,95,44,.45) }


/* Reglagen. Appen har inga sedan tidigare — de andra måtten skrivs som tal
   med enhet. Här dras det i stället, för värdet syns på duken medan man
   drar och en siffra hade varit ett omväg dit. */
.ctrl--range {
  -webkit-appearance:none; appearance:none;
  width:100%; height:1.5rem; padding:0; background:transparent; border:0;
}
.ctrl--range::-webkit-slider-runnable-track {
  height:.25rem; border-radius:999px; background:var(--surface-2);
}
.ctrl--range::-moz-range-track {
  height:.25rem; border-radius:999px; background:var(--surface-2);
}
.ctrl--range::-webkit-slider-thumb {
  -webkit-appearance:none; appearance:none;
  width:1.375rem; height:1.375rem; margin-top:-.5625rem;
  border-radius:50%; background:var(--primary); border:0; cursor:pointer;
}
.ctrl--range::-moz-range-thumb {
  width:1.375rem; height:1.375rem;
  border-radius:50%; background:var(--primary); border:0; cursor:pointer;
}
.ctrl--range:focus-visible { outline:2px solid var(--accent); outline-offset:4px }

/* Knapparna sist. Schemalägg väger tyngst och står först — det är det man
   kom hit för; att spara ett utkast är vad man gör när man inte är klar. */
/* Klippet under duken. Spelaren är lika bred som duken och står på
   svart: en video har sällan dukens proportioner, och en ljus ram runt
   svarta fält ser ut som en bild som inte laddats. */
.soVideo { max-width:26rem; margin:0 auto 1.5rem }
.soVideo--tom { display:flex; flex-direction:column; align-items:center; gap:.5rem;
  text-align:center }
.soVideo__hint { margin:0; max-width:22rem; line-height:1.45 }
.soVideo__spelare { display:block; width:100%; max-height:26rem; margin:0 0 .5rem;
  background:#000; border-radius:var(--radius-card-sm, 1.25rem) }
.soVideo__meta { margin:0 0 .75rem; font-variant-numeric:tabular-nums }
.soVideo__status { margin:.5rem 0 0; color:var(--accent); font-variant-numeric:tabular-nums }

/* Kopiera och ladda ner, under bildtexten. Två knappar som hör ihop:
   det man tar med sig härifrån till en annan kanal. */
.soExport { justify-content:flex-start; gap:.5rem; flex-wrap:wrap; margin-top:.75rem }
/* Det publicerade kortet. Bilden står där duken annars står och i
   samma mått; flera bilder blir ett rutnät med två i bredd. */
.soPub__imgs { max-width:26rem; margin:0 auto 1rem }
.soPub__imgs img { display:block; width:100%; border-radius:var(--radius-card-sm, 1.25rem) }
.soPub__imgs--flera { display:grid; grid-template-columns:1fr 1fr; gap:.5rem }
.soPub__caption { margin:0; white-space:pre-wrap; line-height:1.55 }
.soPub .soExport { margin-top:1rem }
/* Öppettiderna i Ring upp mig: namn, tider och reglage på en rad. */
.cbDay { display:flex; align-items:center; gap:.75rem; padding:.5rem 0 }
.cbDay + .cbDay { border-top:1px solid var(--line) }
.cbDay__namn { flex:0 0 5.25rem; font-size:.9375rem }
.cbDay.is-off .cbDay__namn { opacity:.55 }
.cbDay__tider { display:flex; align-items:center; gap:.375rem; flex:1 1 auto; min-width:0 }
.cbDay__tider[hidden] { display:none }
.cbDay__tider .ctrl { flex:1 1 0; min-width:0; width:auto; min-height:2.375rem;
  padding:.375rem .5rem; text-align:center; font-variant-numeric:tabular-nums }
/* Klockikonen bort: den tar en tredjedel av fältet och öppnar samma
   väljare som ett tryck i fältet gör. */
.cbDay__tider input::-webkit-calendar-picker-indicator { display:none; -webkit-appearance:none }
.cbDay__till { color:rgba(var(--ink-rgb),.5) }
.cbDay__switch { flex:0 0 auto; margin-left:auto }

/* En rad som förklarar, med tecken. Mörkare än kortet den står i, så den
   läses som en upplysning bredvid inställningarna och inte som en av dem. */
.infoNote { display:flex; align-items:flex-start; gap:.625rem;
  margin:0 0 .5rem; padding:.875rem 1rem; border-radius:.875rem;
  background:rgba(0,0,0,.22); font-size:.8125rem; line-height:1.5;
  color:rgba(var(--ink-rgb),.75) }
.infoNote p { margin:0 }
.infoNote__ico { flex:0 0 auto; width:1rem; height:1rem; margin-top:.125rem;
  stroke:currentColor; fill:none; stroke-width:1.75; stroke-linecap:round; stroke-linejoin:round }

.soActions { display:grid; gap:.625rem; margin-top:2rem }
.soDone { margin-top:2rem; text-align:center }
.soDone .btn { margin-top:.75rem }

@media (min-width:38rem) {
  .soActions { grid-template-columns:1fr 1fr }
}

/* ==================== flytande etiketter ==========================
   Etiketten inuti fältet i stället för ovanför det.

   Sex fält gav förut sex versala rader ovanför sex rutor: tolv rader
   för sex uppgifter, och etiketten stod kvar och tog plats långt efter
   att den gjort sin nytta. Den behövs innan man fyllt i, inte efter.

   Tomt fält: etiketten står där texten ska stå, i textens storlek. Man
   läser rutan och vet vad den vill ha.

   Ifyllt eller i fokus: etiketten kryper upp i överkanten, liten och
   dämpad, och innehållet tar dess plats. Identiteten finns kvar — det
   är skillnaden mot att bara sätta en placeholder och kalla det en
   etikett. En ifylld ruta där det står "Sedan 2014" och inget annat
   säger inte vilket fält det är.

   Placeholdern är kvar som det den är: ett exempel, synligt först vid
   fokus — alltså när man ska skriva och kan ha nytta av att se hur ett
   svar ser ut. */
.field--float { position:relative; gap:0 }

/* Luften ovanför den lilla etiketten.
 *
 * Måtten var 1,55 och 0,45 rem, och den uppkrupna etiketten hamnade då
 * sju pixlar från rutans överkant. Det räckte tekniskt men läste sig
 * packat: raden låg klistrad i kanten medan värdet under den hade luft åt
 * alla håll, och en spalt med sex sådana fält såg trång ut utan att man
 * kunde peka på vad det var.
 *
 * De tre måtten hänger ihop och ska ändras tillsammans: stoppningen här,
 * etikettens top, och förflyttningen i det uppkrupna läget. Räkningen står
 * vid varje. */
.field--float > .ctrl { padding-top:1.8rem; padding-bottom:.55rem }
.field--float > textarea.ctrl { padding-top:1.9rem }

/* Vilande etikett: mitt i rutan.
 *
 * Uppmätt och inte uträknat. Rutan är 60,6 px hög och etikettens radbox
 * 20 px; med top 1,2 rem blev det 19,2 px över och 21,4 px under, alltså
 * en aning för högt — precis det man ser utan att kunna peka ut varför.
 * 1,3 rem ger 20,8 över och 19,8 under, och det sista halva pixelvärdet
 * åt andra hållet är med flit: etiketter utan underhäng ("Rubrik") har
 * sin synliga massa i radens övre del och ser höga ut på en rad som står
 * geometriskt mitt i.
 *
 * Ändras stoppningen ovanför ändras det här talet med den. */
/* left är rutans stoppning PLUS dess kant. Etiketten ligger absolut mot
   fältets kantlåda och började därför en pixel till vänster om den text
   man skriver — samma ord på två ställen, en pixel isär, vilket syns som
   att raden hoppar när man börjar skriva. */
.field--float > label {
  position:absolute; left:1.0625rem; top:1.3rem; right:2.5rem;
  display:block; font-size:1rem; font-weight:400;
  text-transform:none; letter-spacing:0; line-height:1.25;
  color:rgba(var(--ink-rgb),.5);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  pointer-events:none; transform-origin:left top;
  transition:transform .15s ease, color .15s ease;
}

/* Villkoret är :placeholder-shown, alltså "fältet är tomt". Därför bär
   varje fält ett placeholder-attribut även när det saknar exempel — ett
   blanksteg räcker, och utan det matchar väljaren aldrig. */
/* Uppkrupen etikett: 1,2 - 0,6 = 0,6 rem från rutans överkant, alltså
   nio pixlar luft över ordet i stället för sju. Skillnaden är liten och
   syns i hela appen på en gång. */
.field--float > .ctrl:focus + label,
.field--float > .ctrl:not(:placeholder-shown) + label {
  transform:translateY(-.6rem) scale(.6875);
  font-weight:600; text-transform:uppercase; letter-spacing:.06em;
  color:rgba(var(--ink-rgb),.62);
}

/* Kontroller utan tomt tillstånd — rullgardinen — står alltid i det
   lilla läget. Den har alltid ett värde, och en etikett som väntar på
   att fältet ska fyllas i väntar där förgäves. */
.field--floated > label {
  transform:translateY(-.6rem) scale(.6875);
  font-weight:600; text-transform:uppercase; letter-spacing:.06em;
  color:rgba(var(--ink-rgb),.62);
}
.field--floated > .ctrl--select { padding-top:1.8rem; padding-bottom:.55rem }

.field--float > .ctrl::placeholder { color:transparent }
.field--float > .ctrl:focus::placeholder { color:rgba(var(--ink-rgb),.34) }

/* Tipsknappen på etikettens rad, inte i rutans hörn.

   .5rem var kvar från tiden då etiketten låg ovanför fältet och hörnet
   var enda lediga plats. Med etiketten inne i rutan hamnade i:et elva
   pixlar ovanför ordet det hör till — två saker på samma rad ritade på
   olika höjd, vilket ögat läser som att den ena glidit ur läge.

   Talet ställer tecknets mitt på etikettens mitt i viloläget, och följer
   därför etikettens top: 1,3125 rem när etiketten vilar på 1,2 rem. Det
   gäller även när etiketten krupit upp — värdet står då där etiketten
   stod, och det är värdet i:et ska stå bredvid. */
.field--float > .tip { position:absolute; right:.5rem; top:1.4125rem }

@media (prefers-reduced-motion:reduce) {
  .field--float > label { transition:none }
}

/* Listen med två knappar i högerkanten.

   Rutnätet är annars tre fasta spalter — knapp, rubrik, knapp — och en
   andra knapp i sista spalten hade fått dela på 2,75 rem med den första.
   Sista spalten får därför växa efter sitt innehåll i just den här
   listen, och rubriken behåller resten. */
.bar--two { grid-template-columns:2.75rem 1fr auto }
.bar__btns { display:flex; align-items:center; justify-self:end }

/* ---------- delningsbilden i sidinställningarna ----------
   En rad som svarar på en fråga i stället för ett fält som ställer den.
   Bilden bor i startsektionen; den här raden visar vilken det blev och
   var man byter den. */
.shareRow {
  display:flex; align-items:center; gap:.875rem;
  padding:.875rem; margin-top:1.25rem;
  border:1px solid var(--line); border-radius:var(--radius-control);
  background:var(--card);
}
.shareRow__img {
  flex:0 0 auto; width:4.5rem; height:3rem; object-fit:cover;
  border-radius:calc(var(--radius-control) * .6); display:block;
}
.shareRow__img--tom {
  background:repeating-linear-gradient(135deg,
    rgba(var(--ink-rgb),.05) 0 6px, transparent 6px 12px);
  border:1px dashed var(--line);
}
.shareRow__t { display:flex; flex-direction:column; gap:.1875rem; min-width:0 }
.shareRow__label { font-size:.8125rem; font-weight:600 }
.shareRow__hint { font-size:.75rem; line-height:1.5; color:rgba(var(--ink-rgb),.55) }

/* Rik text: etiketten i rutans överkant, ovanför knappraden.
   Rutan får en remsa att härbärgera den i — knappraden är hopfälld tills
   fältet används, så utan remsan hade etiketten legat på första raden
   text. */
.field--float > .rte { padding-top:1.35rem }

/* Spaltväljaren i en tvåspaltig sektion. Samma form som flikraden ovanför
   men lättare: två rader som ser exakt likadana ut hade lästs som ett fel,
   och den undre är ett led under den övre i betydelse också. */
.seg--cols { margin-top:.5rem; font-size:.8125rem }
.seg--cols button { padding-top:.5rem; padding-bottom:.5rem }

/* ---------- flyttbar listrad ----------
   Handtaget till vänster, tumnageln till höger. Tumnageln var greppet
   förut; en bild ser ut som något man öppnar, inte som något man drar i,
   och den som ville öppna sektionen startade i stället en flytt. */
.listRow__move {
  /* Helt rund, och rund på riktigt: 50 % och inte en pill-radie, som
     bara ser rund ut så länge höjd och bredd råkar stämma. Måtten är
     dessutom låsta i båda riktningarna — en flexrad krymper annars
     bredden på det som får krympa, och cirkeln blir en oval. */
  flex:0 0 2.25rem;
  width:2.25rem; height:2.25rem; min-width:2.25rem;
  border-radius:50%;

  /* Bara en ram. En fylld platta läser sig som en knapp man trycker på,
     och den här trycker man inte på — man drar i den. */
  background:none;
  border:1px solid rgba(var(--ink-rgb),.22);
  color:rgba(var(--ink-rgb),.55);

  display:grid; place-items:center;
  /* Mitt i raden och inte i överkanten. Raden är tre våningar hög och
     handtaget gäller hela raden, inte dess första rad text. */
  align-self:center;
  margin-right:.875rem;

  cursor:grab; touch-action:none;
  transition:border-color .18s, color .18s;
}
.listRow__move svg {
  width:1rem; height:1rem; stroke:currentColor; fill:none;
  stroke-width:1.75; stroke-linecap:round; stroke-linejoin:round;
}
.listRow__move:hover {
  border-color:rgba(var(--ink-rgb),.45);
  color:rgba(var(--ink-rgb),.85);
}
.listRow__move:active { cursor:grabbing }

/* Tumnageln sitter sist i knappen på en flyttbar rad, och behöver därför
   sin marginal på andra hållet än förut. */
.listRow--flyttbar .listRow__thumb.prow__thumb { margin:0 }

/* Tomrummet där handtaget skulle ha stått. Exakt handtagets mått, så
   textspalten börjar på samma punkt i hela listan — se gripSpace i
   listRow. Ingen yta, ingen kant: det ska inte se ut som en knapp som
   råkat bli tom. */
.listRow__gripSpace {
  flex:0 0 2.25rem; width:2.25rem; height:2.25rem; min-width:2.25rem;
  align-self:center;
}

/* Resultatet av bokningsläsningen, under adressen på Tjänster.

   Ingen ram och ingen egen yta: blocket står under det fält det handlar
   om, och en kant där hade varit en ruta i en ruta i en spalt. Linjen
   ovanför räcker för att säga att det som följer är ett svar och inte ett
   fält till. */
.bookStatus {
  margin-top:.25rem; padding-top:1rem;
  border-top:1px solid var(--line);
}

/* ============================== länkarna ==========================
   QR-koden och de fyra sätten att räcka adressen vidare.

   Ingen platta alls. Koden låg först på hårdkodad vit — en avläsare
   letar efter mörkt mot ljust — men en vit bricka mitt i en mörk app är
   ett hål i skärmen, och telefonernas kameror läser inverterade koder
   sedan länge. Nästa steg var en grå platta i knapparnas färg, och då
   blev den i stället en ruta i en ruta: koden har redan sin egen tydliga
   fyrkant och behöver ingen ram omkring sig.

   Kvar står koden själv, i currentColor, centrerad på sidans egen yta.
   Behållaren går hela vägen ut till kanterna och märks inte — den finns
   bara för att hålla koden och adressen i mitten.

   Bredden på koden är ändå begränsad. En QR-kod som fyller en
   skrivbordsskärm är ingen hjälp för någon: den läses från en telefon på
   armlängds avstånd, och där räcker en handflata. */
.lankKort {
  display:flex; flex-direction:column; align-items:center; gap:1rem;
  color:var(--foreground);
  padding:0; margin-top:1.5rem; width:100%;
}
.lankKort__qr { display:block; width:100%; height:auto; max-width:15rem }
.lankKort__url {
  margin:0; font-size:.8125rem; line-height:1.5; text-align:center;
  color:rgba(var(--ink-rgb),.58); word-break:break-all;
}

/* Fyra knappar på en rad, lika breda. Ikonen över ordet: raden ska gå att
   träffa med tummen utan att läsas, och en ikon hittar handen fortare än
   ett ord gör. */
.lankAtg {
  display:grid; grid-template-columns:repeat(4, 1fr); gap:.5rem;
  margin-top:1.25rem;
}
.lankAtg__k {
  display:flex; flex-direction:column; align-items:center; gap:.375rem;
  padding:.875rem .25rem;
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--radius-control); color:var(--foreground);
  font-size:.75rem; font-weight:500; text-decoration:none;
  cursor:pointer; transition:background .12s ease, border-color .12s ease;
}
.lankAtg__k:hover { background:var(--surface-2); border-color:var(--subtle) }
.lankAtg__k:active { transform:translateY(1px) }
/* Strecken och inte ytorna. Appen sätter det per ikonklass och inte en
   gång för alla svg — utan raden här ritades de fyra tecknen ifyllda,
   alltså som mörka klumpar på en mörk knapp. */
.lankAtg__i {
  width:1.25rem; height:1.25rem;
  stroke:currentColor; fill:none; stroke-width:2;
  stroke-linecap:round; stroke-linejoin:round;
}

/* Pris och längd står bredvid varandra i tjänstens ark.

   Två fält som båda rymmer fyra tecken ska inte ta två rader i ett ark
   där höjden är det man har minst av — och de läses dessutom ihop: "600
   kr / 50 min" är ett svar och inte två. */
.svcPair { display:grid; grid-template-columns:1fr 1fr; gap:0 .75rem }

/* ============================== ikonväljaren ======================
   Raden i formuläret visar det valda tecknet, dess namn och vilken grupp
   det kom ur. Rutnätet med alla tecken bor i ett ark: fyrtioåtta ikoner i
   spalten hade gjort rubriken och texten till något man rullar förbi. */
.iconRow__box {
  flex:0 0 auto; width:2.5rem; height:2.5rem;
  display:grid; place-items:center;
  border-radius:.625rem; background:var(--surface-2);
  color:var(--accent);
}
.iconRow__box .ikon { width:1.375rem; height:1.375rem; display:block }
/* Tom ruta: tecknet är dämpat och säger "här ska något stå", inte
   "något gick fel". */
.iconRow__tom { width:1.25rem; height:1.25rem; color:rgba(var(--ink-rgb),.3) }

.ikonGrupp + .ikonGrupp { margin-top:1.5rem }
.ikonGrupp__t {
  display:block; margin-bottom:.625rem;
  font-size:.6875rem; font-weight:600; letter-spacing:.06em;
  text-transform:uppercase; color:rgba(var(--ink-rgb),.45);
}
/* Så många som får plats, aldrig smalare än en tumme. Rutnätet ska
   kunna växa med arket utan en mediefråga per bredd. */
.ikonRut {
  display:grid; gap:.375rem;
  grid-template-columns:repeat(auto-fill, minmax(4.5rem, 1fr));
}
.ikonVal {
  display:flex; flex-direction:column; align-items:center; gap:.375rem;
  padding:.75rem .25rem;
  border:1px solid transparent; border-radius:.75rem;
  background:none; cursor:pointer; color:rgba(var(--ink-rgb),.72);
  transition:background .15s, border-color .15s, color .15s;
}
.ikonVal .ikon { width:1.5rem; height:1.5rem; display:block }
.ikonVal span {
  font-size:.625rem; line-height:1.2; text-align:center;
  color:rgba(var(--ink-rgb),.5);
  /* Ett långt namn får bryta, men rutan ska inte växa av det. */
  overflow:hidden; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;
}
@media (hover:hover) and (pointer:fine) {
  .ikonVal:hover { background:rgba(var(--ink-rgb),.055); color:var(--foreground) }
}
/* Det valda tecknet bär accenten, som allt annat valt i appen. */
.ikonVal.is-on {
  border-color:var(--accent); color:var(--accent);
  background:rgba(177,95,44,.08);
}
.ikonVal.is-on span { color:var(--accent) }

/* ---------- startsektionens rad ----------
   Sidans egen rad, inte en sektion bland andra. Den ligger först, går
   inte att flytta och går inte att ta bort — och det syntes förut bara på
   att handtaget saknades, alltså på något som INTE fanns.

   Nu bär den en dämpad platta. Den går kant i kant med raden, för raden
   ÄR bandet; den ligger ett steg upp från sidans yta och inte i en ram,
   så den skiljer sig utan att bli ett kort mitt i en lista. Lite högre än
   grannarna av samma skäl: den bär sidans rubrik, och en aning mer luft
   säger att den väger mer utan att någon behöver läsa varför.

   Innehållet är i övrigt en helt vanlig rad — bild till vänster, chevron
   till höger. Se listRow. */
/* Plattan har luft i alla fyra kanterna. Utan den vågräta låg tumnageln
   mot vänsterkanten och chevronen mot högerkanten, och plattan läste sig
   som ett fel snarare än som en yta raden ligger på. Något mindre i
   sidled än i höjdled: det är en rad och inte ett kort, och en rad ska
   fortfarande börja ungefär där listans andra rader börjar. */
.listRow--start {
  background:var(--surface-2); border-radius:.75rem;
  padding:0 .875rem;
}
.listRow--start .listRow__open { padding:1.125rem 0 }
/* Tumnageln ligger på plattan och inte på sidans yta, och behöver därför
   en egen ton — annars försvinner rutan i raden och stjärnan svävar. Ett
   steg upp, samma håll som allt annat som ligger ovanpå.

   Marginalen mot texten är radens gap; på en flyttbar rad sitter
   tumnageln sist och nollställs där (se .listRow--flyttbar), men den här
   raden har den först och behöver den inte. */
.listRow--start .listRow__thumb.prow__thumb { background:var(--surface) }
/* Ingen linje mellan startraden och den första sektionen: plattan säger
   redan var den slutar, och en linje ovanpå en kant blir två gränser för
   en. */
.listRow--start + .listRow { border-top:0 }

/* Ikonen i en tumnagel utan bild.

   .35 mot ytan var för svagt — rutan lästes som tom i stället för som
   "den här sektionen har ingen bild". Den ska vara dämpad, inte
   osynlig: det är en upplysning och inte en varning. */
.prow__ico { color:rgba(var(--ink-rgb),.62) }
.prow__ico svg { stroke-width:1.9 }

/* ---------- Lägg till sektion, fäst i underkanten ----------
   Listan blir lång och knappen låg sist i den: den som ville lägga till
   en sektion till fick rulla förbi allt hen just byggt. */
.stickyAdd {
  position:sticky; bottom:0; z-index:3;
  margin-top:1.25rem;
  padding:.875rem 0 max(.875rem, env(safe-area-inset-bottom));
  background:var(--background);
  box-shadow:0 -1px 0 0 var(--line);
}
.stickyAdd .btn { width:100% }

/* ============================== mål och rapporter ==================
   Under Synlighet, efter siffrorna. Kortet är samma kort som resten av
   appen; det enda som tillkommer är stapeln och raden under den.

   Stapeln är avsiktligt låg. Den är ett kvitto man läser i förbifarten —
   "det rör sig" — och inte ett diagram: den som vill veta exakt läser
   talen under, och de står med samma vikt som i resten av statistiken. */
.goalList { display:flex; flex-direction:column; gap:.75rem }

.goal { position:relative; padding-bottom:1rem }

.goal__head {
  display:flex; align-items:flex-start; gap:.625rem;
  /* Plats åt papperskorgen i högerkanten. Utan reserven la sig en lång
     rubrik under den och blev oläslig på telefonen. */
  padding-right:2rem;
}
.goal__title {
  flex:1 1 auto;
  font-size:.9375rem; font-weight:600; line-height:1.35;
  letter-spacing:-.01em;
}

/* Etiketten som säger vad raden är. Nått är grönt, ett förslag bär
   accenten: det ena är ett besked, det andra en fråga. */
.goal__flag {
  flex:0 0 auto;
  font-size:.6875rem; font-weight:600; letter-spacing:.02em;
  text-transform:uppercase;
  padding:.1875rem .4375rem; border-radius:.375rem;
  background:rgba(47,107,70,.12); color:var(--ok);
}
.goal__flag--new { background:rgba(177,95,44,.12); color:var(--accent) }

.goal__bar {
  margin:.75rem 0 .5rem;
  height:.375rem; border-radius:999px; overflow:hidden;
  background:rgba(var(--ink-rgb),.08);
}
.goal__fill {
  display:block; height:100%; border-radius:999px;
  background:var(--accent);
  transition:width .4s ease;
}
.goal--done .goal__fill { background:var(--ok) }

.goal__foot {
  display:flex; align-items:baseline; justify-content:space-between; gap:.75rem;
}
.goal__now { font-size:.8125rem; color:rgba(var(--ink-rgb),.62) }
.goal__now b { font-weight:600; color:rgba(var(--ink-rgb),.9) }
.goal__sub {
  font-size:.75rem; color:rgba(var(--ink-rgb),.45);
  text-align:right;
}

/* Verktygets motivering. Står bara på förslag och på det som verktyget
   själv satt — den som skrivit sitt mål behöver ingen förklaring till
   det. */
.goal__why {
  margin:.75rem 0 0; padding-top:.75rem;
  border-top:1px solid var(--line);
  font-size:.8125rem; line-height:1.55; color:rgba(var(--ink-rgb),.62);
}

.goal__choice { display:flex; gap:.5rem; margin-top:.875rem }
.goal__choice .btn { flex:1 1 auto; width:auto }

/* Papperskorgen i hörnet. Liten yta, hel radhöjd att träffa på, och
   dämpad tills man rör den: den ska gå att hitta, inte att snubbla på. */
.goal__del {
  position:absolute; top:.625rem; right:.625rem;
  width:2rem; height:2rem; display:grid; place-items:center;
  border:0; border-radius:.5rem; background:none; cursor:pointer;
  color:rgba(var(--ink-rgb),.3);
}
/* Streck och inte fyllning. Ikonerna ritas som konturer och får sin
   stroke av den ruta de står i; den här rutan gav bara mått, och då
   föll svg:n tillbaka på fylld svart utan kontur — en smal mörk stapel
   där korgen skulle stå. */
.goal__del svg {
  width:1.0625rem; height:1.0625rem;
  stroke:currentColor; fill:none; stroke-width:1.75;
  stroke-linecap:round; stroke-linejoin:round;
}
@media (hover:hover) and (pointer:fine) {
  .goal__del:hover { background:rgba(var(--ink-rgb),.055); color:var(--danger) }
}
.goal__del:active { color:var(--danger) }

/* Knapparna under listan. Två jämnstora och inte en fullbred: ingen av
   dem är skärmens åtgärd — det är siffrorna ovanför man kom för. */
.goalActions { display:flex; gap:.5rem; margin-top:.875rem }
.goalActions .btn { flex:1 1 auto; width:auto }

/* ---------- rapporten i arket ---------- */
.repSum {
  margin:0 0 1rem; font-size:.9375rem; line-height:1.65;
  white-space:pre-wrap;
}
.repList {
  margin:0 0 1.25rem; padding:0; list-style:none;
  display:flex; flex-direction:column; gap:.5rem;
}
.repList li {
  position:relative; padding-left:1rem;
  font-size:.8125rem; line-height:1.55; color:rgba(var(--ink-rgb),.7);
}
.repList li::before {
  content:''; position:absolute; left:0; top:.5rem;
  width:.3125rem; height:.3125rem; border-radius:50%;
  background:var(--accent);
}

/* Talen i ett rutnät. Två spalter på telefonen, tre när det finns plats:
   sex mått i en spalt blir en lista man rullar, och de hör ihop. */
.repGrid {
  display:grid; gap:.75rem;
  grid-template-columns:repeat(2, minmax(0, 1fr));
}
@media (min-width:30rem) {
  .repGrid { grid-template-columns:repeat(3, minmax(0, 1fr)) }
}
.repCell {
  display:flex; flex-direction:column; gap:.125rem;
  padding:.75rem; border-radius:.625rem;
  background:var(--surface-2);
}
.repCell__k { font-size:.6875rem; color:rgba(var(--ink-rgb),.55); line-height:1.3 }
.repCell__v { font-size:1.25rem; font-weight:600; letter-spacing:-.02em }
.repCell__d { font-size:.6875rem; color:rgba(var(--ink-rgb),.45) }

.repGoal { margin-top:1.25rem }
.repGoal__h {
  display:block; margin-bottom:.5rem;
  font-size:.6875rem; font-weight:600; letter-spacing:.04em;
  text-transform:uppercase; color:rgba(var(--ink-rgb),.45);
}
.repGoal__r {
  display:flex; justify-content:space-between; gap:.75rem;
  padding:.5rem 0; border-bottom:1px solid var(--line);
  font-size:.8125rem; line-height:1.4;
}
.repGoal__r:last-of-type { border-bottom:0 }
.repGoal__r > span:last-child {
  flex:0 0 auto; color:rgba(var(--ink-rgb),.55); white-space:nowrap;
}