/*
  GLOBAL, DEFINITIV fix — samma buggmönster har nu setts TRE gånger i
  den här appen (.icon-text-btn svg, .key-badge, .count-badge): så
  fort en komponent-specifik regel sätter display på ett element utan
  att uttryckligen undanta [hidden], vinner den regeln (FÖRFATTARENS
  ursprung går alltid före webbläsarens EGEN ursprungsstilmall i
  kaskaden, oavsett specificitet) över webbläsarens inbyggda
  [hidden] { display: none; } — vilket gör vanlig .hidden = true/false
  i JavaScript overksam, tyst, utan något fel någonstans.

  I stället för att fortsätta jaga nya enskilda instanser av samma
  problem (vilket redan hänt tre gånger) löses HELA klassen av buggar
  här, en gång för alla: en enda regel, tidigt i filen så denririktigt
  syns, som TVINGAR [hidden] att alltid vinna, oavsett vilken
  komponent-regel som annars skulle satt display på elementet.
  !important är motiverat här specifikt eftersom syftet är att
  ÅTERSTÄLLA webbläsarens egna, avsedda beteende — inte att åsidosätta
  någon medveten designregel.

  De tidigare, komponent-specifika reglerna (.icon-text-btn svg[hidden],
  .key-badge[hidden]) ligger kvar som dokumentation av var problemet
  upptäcktes första och andra gången, men är från och med nu överflödiga
  — den här regeln vinner ändå.
*/
[hidden] {
    display: none !important;
}

:root {
    --bg: #E2EEF5;
    --panel: #FFFFFF;
    --line: #B6CBD8;
    --text: #000000;
    --muted: #47687D;
    --primary: #00375B;
    --primary-dark: #000000;
    --danger: #C10B25;
    --shadow: 0 1px 3px rgba(0, 55, 91, 0.14);
    --radius: 10px;
}

* {
    box-sizing: border-box;
}

/*
  html/body = exakt en skärmhöjd, utan att ÄNDA sidan själv kan bli
  scrollbar (overflow: hidden på BÅDA) — bara enskilda paneler INUTI
  appen (vänster-/höger-/AI-panelen, grafytan, Mermaid-panelen) ska ha
  sin egen scrollning, aldrig webbläsarfönstret som helhet.

  Tidigare byggde detta på en HÅRDKODAD siffra i stället: .app-layout
  hade height: calc(100vh - 66px), en gissning om verktygsfältets
  höjd. Den gissningen höll bara så länge verktygsfältets knappar fick
  plats på EN rad — så fort de blev för många/breda för fönstret och
  radbröts till två rader (verktygsfältets RIKTIGA höjd blev t.ex.
  100px, inte 66px) stämde inte längre summan, och HELA sidan (inte en
  enskild panel) fick en egen, oavsiktlig scrollningslista.

  Lösningen är layout utan någon gissning alls: body är en flex-kolumn
  över hela skärmhöjden, verktygsfältet tar den höjd det FAKTISKT
  behöver (flex: 0 0 auto på .toolbar, oavsett om det blir en eller
  flera rader), och .app-layout fyller ALLT som blir kvar (flex: 1 1
  auto) — se .toolbar/.app-layout nedan. Den enda delen som kan
  scrolla är nu under appens EGEN kontroll (panelerna), aldrig sidan.
*/
html,
body {
    height: 100%;
    overflow: hidden;
}

body {
    margin: 0;
    min-width: 320px;
    display: flex;
    flex-direction: column;
    color: var(--text);
    background: var(--bg);
    font-family: Inter, "Segoe UI", Roboto, Arial, sans-serif;
    font-size: 14px;
}

button,
select,
input,
textarea {
    font: inherit;
}

button {
    border: 1px solid #859BAC;
    border-radius: 7px;
    padding: 8px 11px;
    background: #FFFFFF;
    color: #000000;
    cursor: pointer;
    /*
      position: relative på ALLA knappar (utan att sätta top/left/etc.)
      har noll visuell effekt i sig själv — men gör det möjligt för en
      enskild knapp att placera en .key-badge (se den klassen) absolut
      positionerad i sitt hörn, utan att varje knapptyp behöver sätta
      det själv.
    */
    position: relative;
    /*
      Text i en knapp ska aldrig radbryta, oavsett hur trång knappens
      omgivning blir (en smal vänsterpanel, en hoprad rad av toolbar-
      knappar) — en radbruten knapp ser trasig ut och blir svårare att
      läsa. Knappen får i stället bli så bred den behöver, eller
      knapparna radbryter SINSEMELLAN (flex-wrap på den omslutande
      raden, satt på respektive plats), snarare än att en enskild
      knapps EGEN text bryts mitt i.
    */
    white-space: nowrap;
    transition: background 0.15s, border-color 0.15s, transform 0.1s;
}

    button:hover {
        background: #E2EEF5;
        border-color: #47687D;
    }

    button:focus-visible {
        outline: 3px solid rgba(0, 55, 91, 0.28);
        outline-offset: 2px;
    }

    /*
      Knappen som Enter skickar in ett formulär till (se withEnterFeedback
      i app.js) — en stående, svagare ring, SKILT från :focus-visible
      ovan, eftersom den ska synas ÄVEN när knappen inte har fokus (dvs.
      hela tiden medan dialogen/panelen är öppen, inte bara när man
      råkar ha tabbat till just den knappen).
    */
    button.default-action {
        outline: 2px solid rgba(0, 55, 91, 0.35);
        outline-offset: 2px;
    }

    /*
      Samma "tangent-bricka" som .key-badge (se den klassen längre ner)
      ger andra knappar för sina genvägar — men som ::after i stället
      för ett riktigt <span>-barn, eftersom flera av dessa knappars
      textContent skrivs om i JavaScript (t.ex. confirmDialogOkBtn, vars
      text byts ut per anrop av showConfirmDialog) — ett riktigt
      barn-element skulle då raderas varje gång. ⏎ (U+23CE) är samma
      symbol tangentbord/OS vanligtvis använder för Enter-tangenten.
    */
    button.default-action::after {
        content: "⏎";
        position: absolute;
        right: -5px;
        bottom: -5px;
        min-width: 13px;
        height: 13px;
        padding: 0 2px;
        display: flex;
        align-items: center;
        justify-content: center;
        background: #00375B;
        color: #FFFFFF;
        border: 1.5px solid #FFFFFF;
        border-radius: 4px;
        font-size: 9px;
        font-weight: 700;
        line-height: 1;
        pointer-events: none;
        z-index: 1;
    }

    /*
      Körs som en kort puls (se default-action-pulse nedan) när Enter
      faktiskt trycks i ett fält — förstärker den stående ringen ovan
      tillfälligt så det syns att knappen "trycktes ned", ungefär som
      en riktig musklickning redan syns via button:active ovan.
    */
    button.default-action-pressed {
        animation: default-action-pulse 0.35s ease-out;
    }

    @keyframes default-action-pulse {
        0% {
            outline: 2px solid rgba(0, 55, 91, 0.35);
            outline-offset: 2px;
        }

        35% {
            outline: 4px solid rgba(0, 55, 91, 0.75);
            outline-offset: 3px;
        }

        100% {
            outline: 2px solid rgba(0, 55, 91, 0.35);
            outline-offset: 2px;
        }
    }

    button:active {
        transform: translateY(1px);
    }

    button:disabled {
        opacity: 0.48;
        cursor: not-allowed;
    }

    button.primary {
        color: #FFFFFF;
        border-color: var(--primary);
        background: var(--primary);
    }

        button.primary:hover {
            color: #FFFFFF;
            border-color: var(--primary-dark);
            background: var(--primary-dark);
        }

    button.danger {
        color: #FFFFFF;
        border-color: var(--danger);
        background: var(--danger);
    }

        button.danger:hover {
            color: #FFFFFF;
            border-color: #000000;
            background: #000000;
        }

input,
select,
textarea {
    width: 100%;
    border: 1px solid #859BAC;
    border-radius: 7px;
    padding: 8px 9px;
    color: #000000;
    background: #FFFFFF;
}

textarea {
    resize: vertical;
}

    input::placeholder,
    textarea::placeholder {
        color: #47687D;
        opacity: 1;
    }

    input:focus,
    select:focus,
    textarea:focus {
        outline: 3px solid rgba(0, 55, 91, 0.22);
        border-color: var(--primary);
    }

.toolbar {
    /*
      flex: 0 0 auto — tar ALLTID exakt den höjd dess eget innehåll
      kräver (en rad eller flera, se kommentaren vid body ovan), aldrig
      mer eller mindre. Growth/shrink förbjudna (0 0): om .app-layout
      nedan skulle sakna utrymme är det DEN som ska krympa/scrolla,
      inte verktygsfältet.
    */
    flex: 0 0 auto;
    min-height: 66px;
    display: flex;
    align-items: center;
    gap: 18px;
    padding: 9px 15px;
    border-bottom: 1px solid var(--line);
    background: #FFFFFF;
    box-shadow: var(--shadow);
    position: relative;
    z-index: 5;
}

#projectSelect {
    max-width: 160px;
}

.project-presence-warning {
    font-size: 12px;
    color: #8A6D00;
    white-space: nowrap;
}

/*
  toolbar-actions ska ligga MITT I det utrymme som blir kvar bredvid
  toolbar-right (loggan/.brand togs bort 2026-10 — se historiken om du
  undrar varför kommentaren inte längre nämner den). flex: 1 1 auto gör
  att knappgruppens EGEN yta fyller precis det utrymmet, och
  justify-content: center centrerar knapparna inuti den ytan.
*/
.toolbar-actions,
.toolbar-right {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 7px;
}

.toolbar-actions {
    flex: 1 1 auto;
    justify-content: center;
}

.toolbar-right {
    flex: 0 0 auto;
}

.inline-label {
    /*
      position: relative så att en eventuell .key-badge-row (se den
      klassen) kan positioneras absolut MOT just den här etiketten,
      utan att lägga till egen bredd/höjd i flödet.
    */
    position: relative;
    display: flex;
    align-items: center;
    gap: 5px;
    color: var(--muted);
    font-size: 12px;
}

    .inline-label select {
        width: auto;
        padding: 7px;
    }

/*
      Modifier för kryssrute-etiketter (t.ex. "Ständig följsamhet") där
      kryssrutan och texten ska sitta ihop utan mellanrum, till skillnad
      från t.ex. Layout-etiketten (label + select) som behåller gap:5px.
    */
.inline-label-checkbox {
    gap: 7px;
}

    .inline-label-checkbox input {
        width: auto;
        margin: 0;
    }

/*
      Extra mellanrum FÖRE etiketten (utöver .graph-controls-groups
      vanliga gap:5px mellan alla barn), så att "Följsamhet" upplevs
      som en egen, fristående funktion snarare än ytterligare en
      layoutkontroll i klungan med Layout-väljaren/Struktur-knappen.
      Samma mått (10px) som används för att skilja de tre stora
      knappklungorna åt i .graph-controls.
    */
.inline-label-separated {
    margin-left: 10px;
}

/*
  Generell variant av samma mellanrum som .inline-label-separated ovan,
  fast för godtyckliga element (inte bara <label>) — används i
  graph-footer för att dela upp den långa knappraden i tydliga grupper:
  urvalsväljarna | arrangera-knapparna | Gör synliga/osynliga/Markera/
  Avmarkera | Ta bort.
*/
.control-gap-before {
    margin-left: 10px;
}

.app-layout {
    /*
      flex: 1 1 auto + min-height: 0 (i stället för den tidigare
      height: calc(100vh - 66px), en gissning om verktygsfältets höjd
      — se kommentaren vid body ovan) fyller EXAKT det utrymme som blir
      kvar under verktygsfältet, oavsett hur högt det faktiskt blev.
      min-height: 0 är nödvändigt: flex-items standard (min-height:
      auto) skulle annars vägra krympa under sitt eget innehålls
      höjd, vilket är precis vad grafytan/panelerna (som redan har sin
      EGEN scrollning, overflow: hidden/auto) behöver kunna göra.
    */
    flex: 1 1 auto;
    min-height: 0;
    display: grid;
    grid-template-columns: var(--left-width, 230px) 6px minmax(350px, 1fr) 6px var(--right-width, 310px) 6px var(--ai-width, 310px);
    grid-template-rows: minmax(0, 1fr) 6px var(--mermaid-height, 285px);
    overflow: hidden;
}

.left-panel,
.right-panel {
    overflow-y: auto;
    /*
      overflow-x: auto (i stället för standardvärdet visible) PLUS
      min-width: 0 (i stället för rutnätets egna standard min-width:
      auto, som annars räknar ut ett golv utifrån INNEHÅLLETS egen
      minsta bredd) är vad som gör det möjligt att dra panelen mycket
      smalare än sitt innehåll utan att innehållet själv krymper/
      radbryter om — se .panel-scroll-inner nedan, som i stället ger
      innehållet ett EGET, fast golv och låter PANELEN scrolla
      horisontellt när den blir smalare än det.
    */
    overflow-x: auto;
    min-width: 0;
    background: var(--panel);
}

/*
  Satt på en INRE wrapper (inte panelen själv, se ovan) runt panelens
  faktiska innehåll: ett eget minimimått som innehållet aldrig krymper
  under, oavsett hur smal panelen själv dras (se LAYOUT_LIMITS i
  app.js, som tillåter panelerna att dras ner mot nära noll — "nästan
  försvinner helt"). Blir panelen smalare än detta mått scrollar den
  i stället horisontellt, utan att någon text eller knapprad radbryter
  om på ett sätt den inte redan gör vid dagens minsta tillåtna bredd.

  250px matchar höger-/AI-panelernas nuvarande minsta bredd (se
  rightWidth/aiWidth i LAYOUT_LIMITS); #leftPanel får ett eget, smalare
  golv nedan som matchar DESS nuvarande minsta bredd (200px) i stället.
*/
.panel-scroll-inner {
    min-width: 250px;
}

#leftPanel > .panel-scroll-inner {
    min-width: 200px;
}

.left-panel {
    grid-column: 1;
    grid-row: 1 / -1;
    border-right: 1px solid var(--line);
}

.right-panel {
    grid-column: 5;
    grid-row: 1 / -1;
    border-left: 1px solid var(--line);
}

/*
  #aiPanel delar .right-panel-klassen med #rightPanel (samma utseende:
  scroll, bakgrund, vänsterkant) men behöver en EGEN kolumn-fallback
  (specifikt ID-väljare vinner över klassväljaren ovan) — JS sätter
  ändå alltid den riktiga platsen via inline style (se applyLayout i
  app.js), det här är bara vad som gäller i den korta stund innan JS
  hunnit köra.
*/
#aiPanel {
    grid-column: 7;
}

    .left-panel.hidden,
    .right-panel.hidden {
        display: none;
    }

/*
  Standardplacering i rutnätet (alla paneler synliga). JS räknar om
  grid-template-columns/rows samt varje barns grid-column/grid-row när
  en panel visas/döljs, så att rutnätet aldrig innehåller tomma spår.
*/
#leftSplitter {
    grid-column: 2;
    grid-row: 1 / -1;
}

#rightSplitter {
    grid-column: 4;
    grid-row: 1 / -1;
}

#aiSplitter {
    grid-column: 6;
    grid-row: 1 / -1;
}

#mermaidSplitter {
    grid-column: 3;
    grid-row: 2;
}

.splitter {
    background: var(--line);
    transition: background 0.15s;
}

    .splitter:hover,
    .splitter.dragging {
        background: var(--primary);
    }

    .splitter:focus-visible {
        outline: 3px solid rgba(0, 55, 91, 0.28);
        outline-offset: -3px;
    }

    .splitter.hidden {
        display: none;
    }

.splitter-vertical {
    cursor: col-resize;
}

.splitter-horizontal {
    cursor: row-resize;
}

.panel-section {
    padding: 14px;
    border-bottom: 1px solid var(--line);
}

    .panel-section h2,
    .properties-header h2 {
        margin: 0 0 10px;
        color: #00375B;
        font-size: 14px;
    }

    .panel-section h3 {
        margin: 14px 0 6px;
        color: var(--muted);
        font-size: 12px;
        text-transform: uppercase;
        letter-spacing: 0.04em;
    }

.node-type-list,
.filter-list {
    display: grid;
    gap: 5px;
}

/*
  Mellanrum ovanför typfiltrets kryssruteslista, som annars klistrar sig
  direkt mot läges-väljaren ("är av typen"/"är inte av typen") ovanför —
  till skillnad från övriga kontrollpar i Filter-sektionen, som redan får
  sitt mellanrum via .filter-select/.filter-action-row/.filter-apply-btns
  egna margin-top. Bara .filter-list (inte .node-type-list, som används
  på annat håll i verktygsfältet och inte ska påverkas av det här).
*/
.filter-list {
    margin-top: 5px;
}

.node-type-button {
    width: 100%;
    display: flex;
    align-items: center;
    gap: 8px;
    text-align: left;
}

.type-dot {
    width: 11px;
    height: 11px;
    flex: 0 0 auto;
    border: 1px solid #00375B;
    border-radius: 50%;
}

/*
  Relationstypfiltrets motsvarighet till .type-dot ovan — men relationer
  särskiljs inte av EGEN FÄRG per typ (se graph.js: alla relationer
  ritas i samma linjefärg, bara etiketten skiljer typerna åt), så en
  liten pil (samma form som en relations egen linje+pilspets i grafen)
  räcker som symbol, identisk för varje rad.
*/
.edge-type-icon {
    width: 16px;
    height: 16px;
    flex: 0 0 auto;
    color: #47687D;
}

.filter-row {
    display: flex;
    align-items: center;
    gap: 7px;
    color: #000000;
    font-size: 13px;
}

    .filter-row input {
        width: auto;
        margin: 0;
        accent-color: #00375B;
    }

    .filter-row:has(input:disabled) {
        opacity: 0.48;
        cursor: not-allowed;
    }

/*
  Kryssrutorna som styr vilka fält (namn/typ/beskrivning/egenskaper)
  sökningen i #searchInput matchar mot. Staplas i en kolumn, en per rad,
  samma mönster som .filter-list (nod-/relationstypfiltren).
*/
.search-fields {
    display: grid;
    gap: 5px;
    margin: 8px 0 4px;
}

/*
  Rubrikrad med en knapp till höger om h2 (Filter + "Nollställ").
  h2:ns egen bottenmarginal nollställs här och flyttas till raden, så
  att knappen och rubriken ligger på exakt samma baslinje.
*/
.section-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin: 0 0 10px;
}

    .section-header h2 {
        margin: 0;
    }

/*
  Kompakt knapp för rubrikrader, mindre än en vanlig knapp så att den
  inte konkurrerar visuellt med sektionens egentliga innehåll. Mindre
  via padding, INTE via en egen font-size — texten ska vara lika stor
  som i alla andra knappar/kryssrutor/radioknappar i panelen (se
  FILTER_TEXT_SIZE-resonemanget vid .filter-select nedan), annars ser
  storleksskillnaden ut som en olikhet i vikt/betydelse snarare än bara
  knappens yta.
*/
.small-btn {
    flex: 0 0 auto;
    padding: 4px 8px;
    font-size: 13px;
}

/*
  Två kryssrutor staplade en per rad, samma mönster som .search-fields/
  .filter-list: urvalet högst upp i Filter-sektionen (synliga/osynliga,
  markerade/ej markerade — se index.html och bindScopeCheckboxPair i
  app.js). En rad var, i stället för sida vid sida, så att panelen inte
  behöver vara bredare än den längsta ENSKILDA kryssrute-etiketten.
  filter-row-group-spaced särskiljer det andra paret visuellt från det
  första, som annars skulle se ut som EN grupp med fyra alternativ i
  stället för två separata val.
*/
.filter-row-group {
    display: grid;
    gap: 5px;
    margin-top: 5px;
}

.filter-row-group-spaced {
    margin-top: 10px;
}

/*
  Fristående väljare som bara tar den plats deras egen text behöver, i
  stället för att fylla panelens bredd: filtrens läges-väljare
  ("innehåller"/"inte innehåller", "är av typen"/"är inte av typen",
  "är inom"/"är inte inom").

  display: block håller dem på egen rad (de ligger direkt efter en <p>
  ovanför, men inget hindrar dem från att annars radera in bredvid ett
  syskonelement). width: fit-content gör att var och en bara blir så
  bred som dess egen bredaste option kräver, inte panelens fulla bredd.
  <select> är normalt redan innehållsbred som block-element (till
  skillnad från en vanlig <div>), men fit-content skrivs ut explicit i
  stället för att förlita sig på den standarden.
*/
.filter-select {
    display: block;
    width: fit-content;
    /*
      max-width: 100% är en säkerhetsspärr, inte huvudregeln:
      vänsterpanelens bredd går att dra i (se .left-panel), och en
      innehållsbred meny måste kunna krympa tillbaka om panelen görs
      smalare än menyns naturliga bredd.
    */
    max-width: 100%;
    margin-top: 5px;
    padding: 6px 8px;
    font-size: 13px;
}

    .filter-select:disabled {
        opacity: 0.48;
        cursor: not-allowed;
    }

/*
  #searchInput ska se ut och vara lika hög som appens övriga textfält,
  inte som webbläsarens inbyggda utseende för type="search" (som Chrome/
  Edge annars ritar högre och rundare, med en egen inbyggd rensa-knapp).
  -webkit-appearance: none plockar bort den native stylingen, så fältet
  faller tillbaka på samma grundregler (input, select, textarea { ... })
  som alla andra textfält i appen.
*/
input[type="search"] {
    -webkit-appearance: none;
    appearance: none;
}

#searchInput {
    margin-top: 5px;
    /*
      En övre gräns på bredden, så fältet inte blir orimligt brett om
      vänsterpanelen dras ut mycket bredare än sin standardbredd
      (230px). width: 100% (ärvt från grundregeln för input/select/
      textarea) fyller fortfarande panelens innehållsbredd upp till
      taket — bara vid en ovanligt bred panel slutar fältet växa, i
      stället för att fortsätta bli bredare och bredare tillsammans med
      panelen.
    */
    max-width: 260px;
}

/*
  Ikon + väljare på samma rad: filtrens "ska:"-del (synlighets- och
  markeringsåtgärd). Ikonen ersätter den textetikett ("Synlighet"/
  "Markering") som tidigare stod bredvid — samma ikonspråk som
  motsvarande knappar i graph-footer (ögat för Gör synliga/osynliga,
  den ikryssade rutan för Markera/Avmarkera), återanvänt här så att
  samma symbol betyder samma sak på båda ställena i appen.
*/
.filter-action-row {
    display: flex;
    align-items: center;
    gap: 7px;
    margin-top: 5px;
}

.filter-action-icon {
    flex: 0 0 auto;
    color: var(--muted);
}

/*
      flex: 0 0 auto + width: auto (i stället för tidigare flex: 1 1
      auto) gör att väljaren bara tar sin egen innehållsbredd, inte
      resten av raden — samma "krymp till innehåll"-princip som
      .filter-select ovan.
    */
.filter-action-row select {
    flex: 0 0 auto;
    width: auto;
    /*
          Samma säkerhetsspärr som .filter-select: vänsterpanelens
          bredd går att dra i (se .left-panel och js/storage.js
          saveLayout), så en innehållsbred meny måste kunna krympa
          tillbaka om panelen görs smalare än menyns naturliga bredd.
        */
    max-width: 100%;
    padding: 6px 8px;
    font-size: 13px;
}

.filter-action-row:has(select:disabled) {
    opacity: 0.48;
    cursor: not-allowed;
}

    .filter-action-row:has(select:disabled) .filter-action-icon {
        opacity: 0.48;
    }

.filter-apply-btn {
    display: block;
    width: fit-content;
    max-width: 100%;
    margin-top: 7px;
    padding: 6px 11px;
    font-size: 13px;
}

/*
  Textfält (siffra) och enheten "px" som text, sida vid sida. Fältet är
  medvetet smalt — tre siffror (t.ex. "120") räcker gott och väl för en
  rimlig textstorlek — i stället för att fylla hela radens bredd.
*/
.font-size-row {
    display: flex;
    align-items: center;
    gap: 7px;
}

    .font-size-row input {
        width: 4.5em;
        flex: 0 0 auto;
    }

    .font-size-row span {
        flex: 0 0 auto;
        color: var(--muted);
    }

.small-muted,
.legend p {
    margin: 5px 0;
    color: var(--muted);
    font-size: 12px;
    line-height: 1.45;
}

.nav-actions {
    display: grid;
    gap: 6px;
    /*
      Samma resonemang som .filter-list ovan: kedjedjupets radioknappar
      kommer direkt efter läges-väljaren ("är inom"/"är inte inom") utan
      någon <p> emellan, och behöver därför sitt eget mellanrum uppåt.
      .nav-actions används bara på det här stället i markupen, så
      regeln påverkar inget annat.
    */
    margin-top: 5px;
}

.graph-area {
    grid-column: 3;
    grid-row: 1;
    min-width: 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
    background: #F3F0EB;
    position: relative;
}

.graph-header {
    min-height: 42px;
    display: flex;
    align-items: center;
    padding: 6px 11px;
    border-bottom: 1px solid var(--line);
    background: rgba(255, 255, 255, 0.94);
}

/*
  Tre grupper (vänster: layoutkontroller, mitten: Anpassa/zoom, höger:
  Glimtar) sprids jämnt över hela headerns bredd med
  justify-content: space-between. flex-wrap gör att grupperna radbryts
  var för sig i stället för att hela raden kollapsar på smala skärmar.
*/
.graph-controls {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex: 1;
    gap: 10px;
    flex-wrap: wrap;
}

.graph-controls-group {
    display: flex;
    align-items: center;
    gap: 5px;
    flex-wrap: wrap;
}

/*
  Rena ikonknappar (geometri-operationerna i graph-footer): samma
  vertikala padding (8px) och kantlinje som vanliga knappar, så att
  höjden blir identisk med t.ex. "Ta bort markerade" — bara den
  horisontella paddingen minskas för ett smalare, kvadratiskt utseende
  eftersom knapparna saknar text. line-height: 0 hindrar SVG:ns
  teckensnittsrelaterade "luft" från att göra knappen någon pixel
  högre än sina textbaserade syskon.
*/
.icon-only-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 8px;
    line-height: 0;
}

    .icon-only-btn svg {
        display: block;
    }

/*
  Liten "tangent-bricka" i knappens nedre högra hörn, för knappar som
  också går att trycka på med tangentbordet — gör BÅDE att en sådan
  knapp går att känna igen som tangentbordsstyrbar, och vilken exakt
  tangent det är, utan att behöva hovra för att se title-attributet.

  aria-hidden på själva <span>:en i HTML (inte något denna regel styr)
  eftersom samma information redan finns i knappens title/aria-label —
  brickan är bara en SYNLIG genväg till den, inte en egen informations-
  källa för skärmläsare.

  pointer-events: none så att brickan aldrig stjäl klick från knappen
  den sitter på, även om den visuellt sticker ut en bit utanför kanten.
  Ärver automatiskt knappens :disabled-nedtoning (opacity, se button
  ovan) eftersom den är ett vanligt barn-element utan egen opacity.
*/
.key-badge {
    position: absolute;
    right: -5px;
    bottom: -5px;
    min-width: 13px;
    height: 13px;
    padding: 0 2px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #00375B;
    color: #FFFFFF;
    border: 1.5px solid #FFFFFF;
    border-radius: 4px;
    font-size: 9px;
    font-weight: 700;
    line-height: 1;
    letter-spacing: 0;
    pointer-events: none;
    z-index: 1;
}

    /*
  Bälte-och-hängslen, samma skäl som .icon-text-btn svg[hidden] (se den
  regeln): display: flex ovan hör till FÖRFATTARENS stilmall, som
  vinner över webbläsarens EGNA [hidden]-regel oavsett specificitet
  (författar-ursprung går alltid före ursprungsstilmallen) — en
  vanlig .hidden = true/false hade alltså inte räckt för att dölja en
  key-badge. removeScopedBtnKeyBadge/deleteSelectedBtnKeyBadge (se
  handleSelection/clearSelection i app.js) sätter därför display
  direkt via inline-stil i stället, men den här regeln skyddar mot
  samma fallgrop om någon annan .key-badge någon gång skulle behöva
  döljas via bara .hidden.
*/
    .key-badge[hidden] {
        display: none;
    }

/*
      Variant för Ångra/Gör om: nedre VÄNSTRA hörnet i stället för högra
      (den enda platsen i appen där det används) — right ärvs annars
      från .key-badge ovan och skulle krocka med left, så den måste
      uttryckligen nollställas här. Bredden anpassar sig automatiskt
      efter innehållet ("Ctrl+Z"/"Ctrl+Y" är längre än de annars
      ensamma bokstäverna/siffrorna i resten av appen), exakt som den
      redan gör för "Del"-brickan på Ta bort-knappen.
    */
.key-badge-bottom-left {
    right: auto;
    left: -5px;
}

/*
  Räknarbricka för Papperskorg-knappen: hur många poster (noder +
  relationer tillsammans) som just nu ligger i papperskorgen. Till
  skillnad från .key-badge ovan sitter den INLINE i knappens flex-rad
  (inte absolutpositionerad i ett hörn) — den representerar ett tal som
  ändras, inte en fast tangent, och döljs helt (via HTML-attributet
  hidden) när papperskorgen är tom i stället för att visa "0".
*/
.count-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 16px;
    height: 16px;
    padding: 0 5px;
    border-radius: 8px;
    background: #C10B25;
    color: #FFFFFF;
    font-size: 10px;
    font-weight: 700;
    line-height: 1;
}

/*
  Samma tangent-brickor som ovan, fast för kontroller som inte är EN
  knapp med ETT hörn att lägga en enda bricka i: urvalsväljarnas
  <select> (tre genvägar var: 1/2/3 och 4/5/6) och Följsamhet-
  kryssrutan. Ligger INUTI samma <label> som själva kontrollen (inte
  som ett fristående syskon-element) — det är avgörande i graph-footer,
  vars .graph-controls-group är utplattad med display: contents (se
  den regeln nedan) för att arrangera-knapparna ska kunna radbryta
  fritt. Som fristående syskon skulle brickorna räknas som ett HELT
  EGET, litet flex-element i det utplattade flödet och kunna radbrytas
  till en helt annan rad än sin meny/kryssruta. Inuti <label>:en följer
  de i stället alltid med som EN atomär enhet, oavsett var raden bryts.

  position: absolute (samma idé som .key-badge på knappar) gör att
  raden ligger OVANPÅ etikettens nedre högra hörn i stället för att ta
  egen plats i flödet — menyn/kryssrutan varken flyttas eller blir
  bredare av att brickorna finns där.
*/
.key-badge-row {
    position: absolute;
    right: -5px;
    bottom: -5px;
    display: inline-flex;
    align-items: center;
    gap: 3px;
    pointer-events: none;
    z-index: 1;
}

    .key-badge-row .key-badge {
        position: static;
    }

/*
  Knappar som kombinerar en liten SVG-ikon med synlig text (t.ex.
  "Alla"/"Synliga"/"Markerade" i graph-header) — till skillnad från
  .icon-only-btn (ingen text alls) behåller dessa knappens vanliga
  padding, bara ikon och text radas upp horisontellt med ett litet
  mellanrum i stället för att bara lita på unicode-tecken i textflödet.
*/
.icon-text-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

    /*
      Ikonstorleken styrs MEDVETET här, centralt via CSS (som vinner
      över de enskilda <svg>-taggarnas egna width/height="14"-attribut
      i index.html) i stället för att behöva ändras i varje enskild
      ikon för sig. 18px valdes efter att Excel-formatikonen (ett "X")
      blev otydlig vid 14px — alla ikoner i den här knapptypen (öppna/
      lägg till/spara-symbolerna OCH format-ikonerna för JSON/Excel/
      Mermaid-kod) höjdes tillsammans för ett enhetligt intryck.
    */
    .icon-text-btn svg {
        display: block;
        flex: 0 0 auto;
        width: 18px;
        height: 18px;
    }

        /*
      Upptäckt via faktiskt testad feedback: regeln ovan (.icon-text-btn
      svg) har HÖGRE specificitet än webbläsarens egen standardregel för
      [hidden] (display: none), och vann därför över den — en knapp med
      FLERA svg-ikoner där man döljer den ena och visar den andra visade
      båda SAMTIDIGT, oavsett vad .hidden sattes till i JavaScript.

      Fungerar som ett bälte-och-hängslen-skydd för eventuella FRAMTIDA
      fall av samma mönster (flera ikoner i en .icon-text-btn som
      växlas via .hidden) — men är INTE längre den enda mekanismen för
      trashBtn:s ikonbyte specifikt. Efter att exakt den här sortens
      kaskad-krock visade sig svårfångad även EFTER den här fixen (en
      tidigare version av samma knapp, se git-historiken/tidigare
      leveranser) sätter enterTrashMode/exitTrashMode i app.js numera
      display DIREKT via inline-stil i stället, vilket alltid vinner
      oavsett CSS-specificitet.
    */
        .icon-text-btn svg[hidden] {
            display: none;
        }

/*
  Egen behållare runt #cy (i stället för att låta #cy själv vara
  positioneringskontext) så att de flytande statusetiketterna nedan
  positioneras mot SJÄLVA RITYTAN — inte mot hela .graph-area, som även
  innehåller knappraden (.graph-header) ovanför. Annars hamnar top-
  positionerade etiketter bakom/ovanpå knapparna i stället för i ritytans
  övre hörn.
*/
.graph-canvas {
    position: relative;
    flex: 1;
    min-height: 0;
}

#cy {
    position: absolute;
    inset: 0;
}

/*
  Knappraden för Mermaid-konvertering, placerad som en "fotrad" längst
  ned i .graph-area (samma flex-kolumn som .graph-header och
  .graph-canvas). Stilmässigt en spegling av .graph-header — samma
  mått/bakgrund — men med border-top i stället för border-bottom
  eftersom den avgränsar sig mot ritytan OVANFÖR i stället för under.
*/
.graph-footer {
    min-height: 42px;
    display: flex;
    /*
      flex-wrap krävs för att knapparna ska kunna radbryta ÖVER HUVUD
      TAGET i den här raden (utan den tvingas alla direkta barn till
      .graph-footer — urvalsväljarna+arrangera-knapparna som EN låda,
      och MMD-knapparna som EN ANNAN låda — ut på en enda rad, oavsett
      hur smalt fönstret blir).

      gap ersätter här .graph-controls-group/.mermaid-actions egna gap
      (5px respektive 7px) med ETT gemensamt värde, eftersom de båda
      slås samman till EN sammanhängande flex-yta via display: contents
      nedan — se den kommentaren för varför.
    */
    flex-wrap: wrap;
    align-items: center;
    gap: 5px;
    padding: 6px 11px;
    border-top: 1px solid var(--line);
    background: rgba(255, 255, 255, 0.94);
}

    /*
  display: contents tar bort SJÄLVA LÅDAN för dessa två omslutande
  <div>ar ur layouten — deras barn (urvalsväljarna, arrangera-
  knapparna, "Ta bort", och de två MMD-knapparna) blir i stället
  DIREKTA flex-barn till .graph-footer, alla i SAMMA radbrytande flöde.

  Det är nödvändigt för att en enskild MMD-knapp ska kunna radbryta för
  sig (i stället för att hela mermaid-actions-lådan hoppar ner som en
  odelbar enhet, eller — det faktiska felet innan denna ändring — ALDRIG
  krymper alls): så länge .mermaid-actions var en egen flex-låda bredvid
  .graph-controls-group, absorberade .graph-controls-group (med sina
  ~30 knappar) hela "krympbehovet" själv via sin egen radbrytning, och
  flexbox-algoritmen behövde då aldrig röra den mycket mindre
  .mermaid-actions-lådan — den behöll sin fulla bredd även när
  fönstret var för smalt för den.

  .graph-controls-group ANVÄNDS ÄVEN i graph-header (tre skilda grupper
  där, som medvetet SKA hållas isär, se den klassens kommentar där) —
  därför begränsas den här regeln med .graph-footer > för att bara
  träffa footerns instans, inte header-grupperna.
*/
    .graph-footer > .graph-controls-group,
    .mermaid-actions {
        display: contents;
    }

/*
  Håller de två MMD-knapparna högerjusterade medan resten av raden
  förblir vänsterjusterad, trots att alla knappar nu ligger i SAMMA
  radbrytande flöde (se display: contents ovan). margin-left: auto på
  bara den FÖRSTA av de två knapparna äter upp allt kvarvarande
  utrymme på DESS rad framför sig — det trycker isär vänstergruppen
  och MMD-paret, snarare än att sprida ut alla knappar jämnt (vilket
  justify-content: space-between på hela raden hade gjort).

  Radbrytningen mellan de två MMD-knapparna påverkas INTE av detta:
  vilken rad ett flex-element hamnar på avgörs av dess ordinarie
  bredd, inte av dess (eventuella) auto-marginal — så de förblir de
  sista knapparna att få plats, och alltså de första att brytas isär,
  precis som innan.

  Ersätter den tidigare .control-gap-before-klassen på just den här
  knappen (som bara gav 10px, mindre synligt mot en redan bred
  auto-marginal) — se index.html.
*/
#graphToMermaidBtn {
    margin-left: auto;
}

/*
  Enhetlig stil för de tre statusmeddelandena som ligger som flytande
  etiketter direkt ovanpå grafytan: #selectionInfo (uppe till vänster),
  #saveStatus (uppe till höger) och #statusMessage (nere till vänster,
  import/export/borttagning m.m.). Grafytan har ingen dölj-knapp, så de
  syns alltid oavsett vilka paneler som råkar vara dolda.

  Bakgrunden är MEDVETET samma som .graph-area:s egen bakgrundsfärg
  (#F3F0EB) i stället för vit eller en färgkodad success/error-bakgrund
  — bara vanligt utformad text mot en enhetlig bakgrund, så den syns
  tydligt oavsett vilken nod eller relation den råkar hamna ovanför.
  #statusMessage ärver även den delade .message-klassen (för min-height/
  padding/success-error-färgning i ANDRA sammanhang, som dialogen och
  egenskapspanelen) — de två reglerna längst ner nollställer just den
  färgade bakgrunden för .success/.error-lägena HÄR, utan att röra
  .message.success/.message.error någon annanstans.
*/
.graph-status {
    position: absolute;
    z-index: 25;
    margin: 0;
    max-width: min(320px, calc(100% - 24px));
    padding: 6px 12px;
    border-radius: 8px;
    background: #F3F0EB;
    color: var(--muted);
    font-size: 12px;
    overflow-wrap: break-word;
    pointer-events: none;
}

    .graph-status:empty {
        display: none;
    }

.graph-status-top-left {
    top: 2px;
    left: 2px;
}

.graph-status-top-right {
    top: 2px;
    right: 2px;
}

.graph-status-bottom-left {
    bottom: 2px;
    left: 2px;
}

.graph-status-bottom-right {
    bottom: 2px;
    right: 2px;
    max-width: none;
}

.graph-status-top-center {
    top: 2px;
    left: 50%;
    transform: translateX(-50%);
    max-width: none;
}

/*
  #glimtBanner ligger ovanpå grafytan som de andra .graph-status-
  etiketterna, men är till skillnad från dem interaktiv (namn + en
  "Avsluta glimt"-knapp) — .graph-status sätter annars pointer-events:
  none på hela etiketten (se ovan), vilket annars skulle göra knappen
  oklickbar.
*/
#glimtBanner {
    display: flex;
    align-items: center;
    gap: 10px;
    pointer-events: auto;
}

    #glimtBanner #glimtBannerLabel {
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

/*
  Tre kolumner (etikett, nodantal, relationsantal) som radar upp sig
  jämnt horisontellt — varje kolumn breddas efter sitt bredaste
  innehåll, vilket en <table> ger automatiskt utan extra uträkning.
  Mellanrummet MELLAN kolumnerna kommer uteslutande från padding-left på
  respektive cell (inte border-spacing), så border-collapse kan vara
  collapse utan att kolumnerna hamnar för tätt.
*/
.graph-counts-table {
    border-collapse: collapse;
}

    .graph-counts-table td {
        padding: 0 0 0 12px;
        white-space: nowrap;
        text-align: left;
    }

        .graph-counts-table td:first-child {
            padding-left: 0;
        }

#statusMessage {
    padding: 6px 12px;
}

    #statusMessage.success,
    #statusMessage.error {
        color: var(--muted);
        background: #F3F0EB;
        border-top: 0;
    }

.node-tooltip {
    position: absolute;
    z-index: 20;
    max-width: 260px;
    padding: 8px 10px;
    border: 1px solid var(--line);
    border-radius: 7px;
    background: #FFFFFF;
    color: #000000;
    font-size: 12px;
    line-height: 1.45;
    white-space: pre-line;
    box-shadow: var(--shadow);
    pointer-events: none;
    transform: translate(-50%, 0);
}

    .node-tooltip.hidden {
        display: none;
    }

.properties-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 13px;
    border-bottom: 1px solid var(--line);
}

.properties-panel {
    padding: 13px;
}

/*
  "Lägg i papperskorgen" (deleteSelectedBtn, se index.html) flyttad hit
  från properties-header (högst upp) — ligger nu direkt efter formens
  EGNA knapprad (.property-actions: Spara ändringar/Återställ, se
  renderNodeProperties m.fl. i app.js) så att alla tre knapparna läses
  som EN sammanhängande åtgärdsrad längst ned i panelen, i stället för
  att delete-knappen sitter isolerad bredvid rubriken. Ingen
  top-padding: properties-panel har redan 13px padding i botten, vilket
  ger exakt samma luft som mellan formens EGNA fält.
*/
.properties-footer {
    display: flex;
    gap: 7px;
    padding: 0 13px 13px;
}

.property-form {
    display: grid;
    gap: 12px;
}

    .property-form label,
    dialog label {
        display: grid;
        gap: 5px;
        color: #00375B;
        font-size: 12px;
        font-weight: 600;
    }

.readonly-id {
    overflow-wrap: anywhere;
    padding: 8px;
    border-radius: 6px;
    color: #00375B;
    background: #E2EEF5;
    font-family: ui-monospace, Consolas, monospace;
    font-size: 11px;
}

/*
  Samma bakgrund/känsla som .readonly-id ovan, men för en LISTA av
  namn (t.ex. medlemmarna en hopslagen kluster-relation representerar,
  se renderCollapsedEdgeProperties i app.js) — vanlig text, inte
  monospace, eftersom det här är namn att läsa, inte ID:n att kopiera.
*/
.readonly-id-list {
    overflow-wrap: anywhere;
    padding: 8px 8px 8px 24px;
    border-radius: 6px;
    color: #00375B;
    background: #E2EEF5;
    font-size: 13px;
    margin: 0;
}

    .readonly-id-list li {
        padding: 2px 0;
    }

/*
  .editable-id-list (renderCollapsedEdgeProperties i app.js): samma
  lista som .readonly-id-list, men varje rad har EN knapp för att
  öppna relationsdialogen för just den enskilda relationen raden
  representerar — radens namn och knapp läggs därför bredvid varandra
  i stället för att bara vara läsbar text.
*/
.editable-id-list li {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}

    .editable-id-list .edit-collapsed-edge-btn {
        flex: 0 0 auto;
        padding: 2px 10px;
        font-size: 12px;
        border-radius: 5px;
        border: 1px solid #9FC1D6;
        background: #F6FBFD;
        color: #00375B;
        cursor: pointer;
    }

        .editable-id-list .edit-collapsed-edge-btn:hover {
            background: #D7EAF2;
        }

.property-actions {
    display: flex;
    gap: 7px;
    margin-top: 2px;
}

.empty-state {
    padding: 38px 14px;
    color: var(--muted);
    text-align: center;
    line-height: 1.5;
}

.empty-icon {
    color: #859BAC;
    font-size: 36px;
}

.mermaid-panel {
    grid-column: 3;
    grid-row: 3;
    min-height: 0;
    display: flex;
    flex-direction: column;
    border-top: 1px solid var(--line);
    background: #FFFFFF;
}

    .mermaid-panel.hidden {
        display: none;
    }

/*
  .mermaid-actions layoutas inte längre av sig själv — se
  display: contents-regeln vid .graph-footer, som flackar ut dess
  knappar till direkta flex-barn i footerns EGNA radbrytande flöde
  (det är det som faktiskt löser att en enskild MMD-knapp kan brytas
  till en ny rad). Den här selektorn finns bara kvar som namngiven
  krok ifall gruppen som helhet behöver egen styling igen någon gång.
*/

#mermaidEditor {
    flex: 1;
    min-height: 0;
    border: 0;
    border-radius: 0;
    padding: 12px 15px;
    resize: none;
    color: #FFFFFF;
    background: #00375B;
    font-family: ui-monospace, "Cascadia Code", Consolas, monospace;
    font-size: 13px;
    line-height: 1.5;
}

    #mermaidEditor:focus {
        outline: 3px solid rgba(193, 11, 37, 0.5);
        outline-offset: -3px;
    }

.message {
    min-height: 22px;
    padding: 4px 14px;
    color: var(--muted);
    font-size: 12px;
}

    .message.error {
        color: #C10B25;
        background: #FFFFFF;
        border-top: 1px solid #C10B25;
    }

    .message.success {
        color: #00375B;
        background: #E2EEF5;
        border-top: 1px solid #B6CBD8;
    }

dialog {
    width: min(480px, calc(100vw - 30px));
    padding: 0;
    border: 1px solid #B6CBD8;
    border-radius: 12px;
    background: #FFFFFF;
    box-shadow: 0 18px 70px rgba(0, 55, 91, 0.32);
}

    dialog::backdrop {
        background: rgba(0, 55, 91, 0.42);
    }

    dialog form {
        display: grid;
        gap: 13px;
        padding: 16px;
    }

.dialog-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

    .dialog-header h2 {
        margin: 0;
        color: #00375B;
        font-size: 17px;
    }

/*
  Radbrytningar (\n) i meddelandetexten ska synas, precis som i
  webbläsarens inbyggda confirm()/alert() den ersätter (se
  showConfirmDialog i app.js) — annars kollapsar HTML dem till mellanslag.
*/
#confirmDialogMessage {
    margin: 0;
    white-space: pre-line;
}

.icon-button {
    padding: 0 8px;
    border: 0;
    background: transparent;
    color: #00375B;
    font-size: 25px;
}

    .icon-button:hover {
        background: #E2EEF5;
        color: #C10B25;
    }

/*
  Versionsdialogen är bredare än relationsdialogen (480px): varje rad
  har fem åtgärder (Återställ + fyra exportvägar) som annars radbryts
  till en trång trappa. Den är inte heller en <form>, så den ärver inte
  "dialog form"-regelns padding/gap ovan — därför en egen layoutregel.
*/
#versionsDialog {
    width: min(760px, calc(100vw - 30px));
    display: none;
}

/*
  Glimt-dialogen: samma mönster som versionsdialogen ovan (ingen
  <form>, alltså ärver den INTE "dialog form"-regelns padding/gap) —
  men utan versionsdialogens extra bredd, eftersom en glimt-rad bara
  har fyra åtgärder (Öppna/Uppdatera/Byt namn/Ta bort, inte fem) och
  ryms inom standardbredden (min(480px, ...), se den generella
  dialog-regeln).
*/
#glimtDialog {
    display: none;
}

/*
  Bildexportdialogen: samma mönster som versionsdialogen ovan (ingen
  <form>, alltså egen padding/gap), fast smalare — innehållet är två
  korta grupper med tre knappar var.
*/
#imageExportDialog {
    width: min(520px, calc(100vw - 30px));
    display: none;
}

    #imageExportDialog[open] {
        display: grid;
        gap: 13px;
        padding: 16px;
    }

/*
  "Kopiera som"-dialogen: samma mönster som bildexportdialogen ovan
  (ingen <form>, alltså egen padding/gap).
*/
#copyAsDialog {
    display: none;
}

    #copyAsDialog[open] {
        display: grid;
        gap: 13px;
        padding: 16px;
    }

/*
  AI-exportdialogen: samma mönster som versionsdialogen/bildexport-
  dialogen ovan (ingen <form>, så den ärver INTE "dialog form"-regelns
  padding/gap) — den här regeln saknades tidigare, vilket gjorde att
  rubrik, text, fält och knappar satt klämda direkt mot varandra och
  mot dialogens kant, utan den marginal alla andra dialoger har.
*/
/*
  Bredare än standarddialogen (480px): två kolumner sida vid sida (se
  .ai-export-columns nedan) i stället för en lång, lodrät stapel, så
  att den slutgiltiga prompttextan (i sig lång nog för att ensam göra
  dialogen hög) inte tvingar fram scroll eller gömmer "Kopiera
  prompt"-knappen under skärmkanten. max-height/overflow är ett sista
  skyddsnät OM fönstret ändå är ovanligt lågt (t.ex. en liten bärbar
  skärm) — hela dialogen scrollar då som helhet, i stället för att
  knappen blir helt oåtkomlig.

  resize: both (kräver overflow skilt från "visible", se MDN) ger
  webbläsarens inbyggda storlekshandtag i nedre högra hörnet — ingen
  egen JS behövs för SJÄLVA storleksändringen. Positionen (flytt via
  header, se makeDialogDraggable i app.js) är separat, eftersom
  resize bara flyttar/ändrar storleken på elementets EGEN box, inte
  var den sitter i fönstret.
*/
#aiExportDialog {
    display: none;
    width: min(880px, calc(100vw - 30px));
    min-width: 480px;
    min-height: 320px;
    resize: both;
    overflow: auto;
}

    #aiExportDialog[open] {
        display: flex;
        flex-direction: column;
        gap: 13px;
        padding: 16px;
        max-height: calc(100vh - 40px);
    }

    /*
      Dragbar via headern (se makeDialogDraggable i app.js) — move-
      pekaren signalerar det INNAN man ens börjat dra. user-select:
      none förhindrar att en snabb dragrörelse råkar markera
      rubriktexten i stället för att faktiskt flytta dialogen.
    */
    #aiExportDialog .dialog-header {
        cursor: move;
        user-select: none;
    }

/*
  flex: 1 (i stället för att bara vara "lagom hög för sitt innehåll")
  gör att kolumnraden fyller ALL höjd dialogens flex-kolumn (se
  #aiExportDialog[open] ovan) har kvar utöver header/urvalsrad/
  promptbibliotek/meddelanderad — avgörande för att en manuell
  förstoring av dialogen (resize ovan) ska ge kolumnerna (och i sin
  tur textareorna, se .ai-export-grow-label nedan) mer utrymme i
  stället för att bara lämna ett tomt fält längst ner. min-height: 0
  är flexboxens kända quirk-fix: utan den vägrar en flex-colum-
  behållare bli KORTARE än sitt innehåll, vilket annars skulle
  motverka hela poängen med overflow/scroll här.
*/
.ai-export-columns {
    display: flex;
    gap: 16px;
    align-items: stretch;
    flex: 1;
    min-height: 0;
}

.ai-export-main-column,
.ai-export-preview-column {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 13px;
    min-width: 0;
}

/*
  align-items: flex-start ovan gör att INGET sträcks ut i sidled som
  standard (annars skulle t.ex. "Prompt"-knappen bli lika bred som hela
  kolumnen, se .ai-export-preview-column) — raderna/etiketterna som
  FAKTISKT ska fylla bredden (urvalsraden, promptbiblioteks-raderna,
  och textarea-etiketterna) får därför detta tillbaka explicit.
*/
.ai-export-main-column > p,
.ai-export-scope-row,
.ai-prompt-library-row,
.ai-export-grow-label {
    align-self: stretch;
}

.ai-export-main-column {
    flex: 1;
}

.ai-export-preview-column {
    flex: 1;
}

/*
  Både "Dina egna instruktioner" (vänster) och "Prompt att klistra in"
  (höger) ska fylla sin kolumns resterande höjd i stället för att ha
  ett fast antal rader (textarean hade annars egen scroll, oberoende
  av hur mycket tomt utrymme som fanns kvar i kolumnen).
*/
.ai-export-grow-label {
    display: flex;
    flex-direction: column;
    flex: 1;
    gap: 4px;
}

    .ai-export-grow-label textarea {
        flex: 1;
        min-height: 110px;
        resize: vertical;
    }

@media (max-width: 760px) {
    .ai-export-columns {
        flex-direction: column;
    }

    .ai-export-grow-label textarea {
        min-height: 160px;
    }
}

    /*
      "Prompt"/"Återställ"-raden ska stå längst till höger i sin kolumn,
      till skillnad från start (vänster) som gäller för övriga rader/
      knappar där (se align-items: flex-start på .ai-export-preview-
      column) — align-self: flex-end (INTE justify-self, som bara gäller
      grid-/absolutpositionerade element, utan effekt i en flex-kolumn
      som den här). Skopad till just den HÄR kolumnen eftersom
      .ai-prompt-library-row återanvänds oförändrad (vänsterjusterad)
      längre upp i samma dialog, för promptbibliotekets egna rader.
    */
    .ai-export-preview-column > .ai-prompt-library-row {
        align-self: flex-end;
    }

/*
  Två urvalsväljare (Synlighet/Markering) OCH nedladdningsknappen på
  samma rad — se aiExportVisibilitySelect/aiExportSelectionSelect/
  downloadAiExportJsonBtn. align-items: flex-end eftersom <label>
  (text ovanför kontrollen, se "dialog label" ovan) är högre än en
  vanlig knapp; utan den skulle knappen annars centreras mitt i hela
  radens höjd i stället för att bottenjusteras mot valen bredvid.
*/
.ai-export-scope-row {
    display: flex;
    align-items: flex-end;
    gap: 13px;
}

.ai-prompt-library-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
}

.ai-prompt-library-row select,
.ai-prompt-library-row input[type="text"] {
    flex: 1;
    min-width: 0;
}

.export-group {
    display: grid;
    gap: 6px;
    padding: 12px;
    border: 1px solid var(--line);
    border-radius: 9px;
    background: #F7FAFC;
}

    .export-group h3 {
        margin: 0;
        color: #00375B;
        font-size: 13px;
    }

    .export-group .inline-label {
        font-size: 12.5px;
    }

.export-buttons {
    display: flex;
    flex-wrap: wrap;
    gap: 7px;
    margin-top: 3px;
}

    /* Lika breda knappar, så de tre formaten läses som jämbördiga val. */
    .export-buttons button {
        flex: 1 1 0;
        min-width: 84px;
    }

/*
  Byt typ-dialogen: samma mönster som #imageExportDialog ovan (ingen
  <form>, alltså egen padding/gap).
*/
#changeTypeDialog {
    width: min(560px, calc(100vw - 30px));
    display: none;
}

    #changeTypeDialog[open] {
        display: grid;
        gap: 13px;
        padding: 16px;
    }

/*
  Byt typ-alternativen: FYRA vanliga knappar i en kolumn i stället för
  radioknappar + text. Ett tidigare försök med radioknappar (flex/grid +
  ett textspann) sprängde ut dialogens bredd i webbläsaren trots flera
  omgångars fixar (fieldset->div, min-width: 0 i hela kedjan) — svårt att
  felsöka blint utan en riktig webbläsare. Knappar är ett mycket enklare
  och redan beprövat mönster (samma som .export-buttons ovan): en
  <button> med explicit width: 100% tar ALLTID exakt förälderns bredd,
  oavsett textens längd, vilket helt eliminerar den typen av
  bredd-beräkningsproblem — det finns ingen flex/grid-krympning att
  resonera om.

  white-space: normal MÅSTE sättas här: den globala button-regeln
  (längre upp i den här filen) sätter white-space: nowrap på ALLA
  knappar som standard (för korta etiketter som "Alla"/"Synliga"), men
  de här knapparnas text är hela meningar som SKA radbryta.
*/
.change-type-policies {
    margin: 0;
    padding: 12px;
    border: 1px solid var(--line);
    border-radius: 9px;
}

    .change-type-policies h3 {
        margin: 0 0 9px 0;
        color: #00375B;
        font-size: 13px;
    }

.change-type-policy-buttons {
    display: flex;
    flex-direction: column;
    gap: 7px;
}

    .change-type-policy-buttons button {
        width: 100%;
        padding: 9px 11px;
        white-space: normal;
        text-align: left;
        font-size: 13px;
        line-height: 1.4;
    }

/*
      [open] krävs: en <dialog> som inte är öppen ska förbli dold, och
      display: grid ovan skulle annars åsidosätta webbläsarens
      inbyggda "display: none" för stängda dialoger och visa innehållet
      mitt i sidan.
    */
#versionsDialog[open] {
    display: grid;
    gap: 13px;
    padding: 16px;
}

#glimtDialog[open] {
    display: grid;
    gap: 13px;
    padding: 16px;
}

.dialog-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 7px;
}

/* Vänsterställd variant, för en ensam åtgärd som inleder ett avsnitt. */
.dialog-actions-start {
    justify-content: flex-start;
}

/*
  Listan över sparade versioner. Varje version är ett eget "kort" med
  sin metadata till vänster och sina åtgärder till höger; korten staplas
  lodrätt och scrollar inuti dialogen i stället för att växa förbi
  skärmkanten när det finns många versioner.
*/
.versions-list {
    display: grid;
    gap: 9px;
    max-height: min(52vh, 420px);
    overflow-y: auto;
}

.version-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 9px;
    padding: 10px 12px;
    border: 1px solid var(--line);
    border-radius: 9px;
    background: #F7FAFC;
}

.version-row-info {
    display: grid;
    gap: 2px;
    min-width: 0;
}

.version-row-title {
    color: #00375B;
    font-size: 13px;
    font-weight: 600;
}

.version-row-meta {
    color: var(--muted);
    font-size: 12px;
}

.version-row-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
}

.versions-empty {
    padding: 18px 12px;
    color: var(--muted);
    font-size: 13px;
    text-align: center;
}

/*
  AI-förslag (panel-sektionen i vänsterpanelen, se js/app.js
  renderAiSuggestionsList): samma rad-/listmönster som .version-row/
  .versions-list ovan, men med en egen färg för ett OGILTIGT förslag
  (kasserat av GraphAiSuggestions.validateSuggestion) så att det syns
  direkt i listan utan att man behöver läsa feltexten först.
*/
.ai-suggestions-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: 8px;
}

/*
  Ingen egen max-height/overflow-y här (till skillnad från en tidigare
  version): listan ska bara flyta direkt i AI-panelen och scrolla
  TILLSAMMANS med den (panelen själv är redan en scrollyta, se
  .right-panel i style.css), inte ha en egen, nästlad scrollruta.
*/
.ai-suggestions-list {
    display: grid;
    gap: 8px;
}

.ai-suggestions-empty {
    padding: 14px 10px;
    color: var(--muted);
    font-size: 13px;
    text-align: center;
}

.ai-suggestion-row {
    display: grid;
    gap: 4px;
    padding: 7px 9px;
    border: 1px solid var(--line);
    border-radius: 9px;
}

.ai-suggestion-row-invalid {
    border-color: #E7B8C2;
    background: #FBF3F5;
}

/*
  Raden själv är en Tab-stoppad (tabindex="0", se renderAiSuggestionsList
  i app.js) — samma ring som button:focus-visible ovan, så det syns
  tydligt VILKET förslag Enter (implementera)/Mellanslag (avfärda) just
  nu skulle träffa.
*/
.ai-suggestion-row:focus-visible {
    outline: 3px solid rgba(0, 55, 91, 0.28);
    outline-offset: 2px;
}

.ai-suggestion-title {
    display: block;
    color: #00375B;
    font-size: 13px;
    font-weight: 600;
}

/*
  Kortad till två rader med "…" (i stället för att visa hela
  resonemanget på raden) — fullständig rationale + alla operationer
  (nya/ändrade/borttagna noder/relationer) visas i stället i
  detaljdialogen, se "Detaljer"-knappen/aiSuggestionDetailsDialog.
*/
.ai-suggestion-rationale {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow: hidden;
    color: var(--muted);
    font-size: 12px;
}

.ai-suggestion-error {
    display: block;
    color: #C10B25;
    font-size: 12px;
    font-weight: 600;
}

/*
  Skiljer sig MEDVETET från .ai-suggestion-error ovan (ingen röd,
  fetstil) — det här är inte ett fel som hindrar Implementera, bara en
  upplysning om att NÅGRA av operationerna kommer hoppas tyst över
  (en föreslagen relation som redan finns), resten genomförs normalt.
*/
.ai-suggestion-skip-note {
    display: block;
    color: var(--muted);
    font-size: 12px;
    font-style: italic;
}

.ai-suggestion-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
}

/*
  "Detaljer"-dialogen (se renderAiSuggestionDetailsBody/handleAiSuggestionAction
  i app.js): fullständig rationale + en lista per operationstyp (nya/
  ändrade/borttagna noder och relationer). Ingen <form>, alltså egen
  padding/gap, samma mönster som bildexport-/"Kopiera som"-dialogerna.
*/
#aiSuggestionDetailsDialog {
    width: min(640px, calc(100vw - 30px));
    display: none;
}

/*
  "Förklara"-dialogen (se js/effect-explanation.js) — bredare och
  högre än standarddialogen, eftersom innehållet är en hel Markdown-
  rapport (rubriker, listor, flera Mermaid-diagram) per Effekt-nod.
*/
#effectExplanationDialog {
    width: min(760px, calc(100vw - 30px));
    display: none;
}

    #effectExplanationDialog[open] {
        display: grid;
        gap: 13px;
        padding: 16px;
        max-height: calc(100vh - 40px);
        overflow-y: auto;
    }

/*
  Grundläggande typografi för den marked.js-renderade Markdown-texten
  — appens globala CSS har inga regler för rena <h1>-<h3>/<ul>/<p>-
  element (allt annat innehåll byggs med egna klasser), så utan dessa
  skulle rubriker/listor se ut som oformaterad brödtext.
*/
.effect-explanation-content h1 {
    margin: 0 0 10px;
    color: #00375B;
    font-size: 20px;
}

.effect-explanation-content h2 {
    margin: 22px 0 8px;
    color: #00375B;
    font-size: 16px;
    border-top: 1px solid #E2EEF5;
    padding-top: 14px;
}

.effect-explanation-content h3 {
    margin: 14px 0 6px;
    color: #00375B;
    font-size: 13.5px;
}

/*
  Används dels för #### Steg 4.1–4.4 (nästlade under ### Steg 4), dels
  för #### Nutid/RCP4,5/RCP8,5 (nästlade under ### Steg 2, se
  describedFieldSection i effect-explanation.js) — samma storlek som h3
  (metodstödets egen rubrikindelning, inte typografisk hierarki, är det
  som skiljer dem åt) och INGET indrag (användarens eget krav 2026-10),
  bara en dovare färg för att ändå visa att de hör till steget/
  tidsperspektivet ovanför.
*/
.effect-explanation-content h4 {
    margin: 10px 0 6px;
    color: #3D6B88;
    font-size: 13.5px;
}

/*
  ##### Bedömning/Beskrivning (se describedFieldSection i
  effect-explanation.js) — nästlade ytterligare ett steg under #### Nutid/
  RCP4,5/RCP8,5, därför mindre än h4 ovan (men inte indragen, se den
  kommentaren).
*/
.effect-explanation-content h5 {
    margin: 8px 0 3px;
    color: #5E86A0;
    font-size: 12.5px;
}

.effect-explanation-content p,
.effect-explanation-content ul,
.effect-explanation-content blockquote {
    margin: 0 0 8px;
    font-size: 13.5px;
    line-height: 1.5;
}

.effect-explanation-content ul {
    padding-left: 20px;
}

.effect-explanation-content blockquote {
    border-left: 3px solid #B6CBD8;
    margin-left: 0;
    padding: 2px 12px;
    color: var(--muted);
}

.effect-explanation-content code {
    font-family: ui-monospace, Consolas, monospace;
    font-size: 12px;
    background: #E2EEF5;
    padding: 1px 4px;
    border-radius: 4px;
}

/*
  ID-länkarna efter varje omnämnd nod-/relationsnamn (se
  .effect-explanation-id-link i effect-explanation.js/app.js) — samma
  monospace-"pill" som .code ovan (ID:n är i praktiken kod, t.ex.
  "n_148"), fast understruken och i märkesfärg för att signalera att de
  är klickbara, till skillnad från vanlig kod.
*/
.effect-explanation-content .effect-explanation-id-link {
    font-family: ui-monospace, Consolas, monospace;
    font-size: 12px;
    background: #E2EEF5;
    padding: 1px 4px;
    border-radius: 4px;
    color: #00375B;
    text-decoration: underline;
    cursor: pointer;
}

/*
  Bedömningsvärden (Hög/Medel/Låg/Ofta/Ibland/Sällan osv) med en
  definition att visa i en vanlig webbläsar-tooltip (title-attribut,
  se formatValue i effect-explanation.js) — en prickad understrykning
  signalerar "för muspekaren hit för mer info", samma mönster som
  klassiska HTML-ordboks-/akronymförklaringar, men utan att ändra
  textens övriga utseende (fortfarande fetstil som vanliga ** -värden).
*/
.effect-explanation-content .effect-explanation-value-tooltip {
    text-decoration: underline dotted;
    text-decoration-color: #8AA0B4;
    text-underline-offset: 3px;
    cursor: help;
}

/*
  Mermaid-diagrammen bäddas in som rena <svg>-element (se
  renderEffectExplanation i app.js, som ersätter varje
  <pre><code class="language-mermaid"> med detta) — centrerade och
  skalade ner om de blir bredare än dialogen.
*/
.effect-explanation-mermaid {
    display: flex;
    justify-content: center;
    margin: 8px 0 14px;
    overflow-x: auto;
}

    .effect-explanation-mermaid svg {
        max-width: 100%;
        height: auto;
    }

/*
  Dokumentationsexport-dialogen (se js/documentation-export.js) — tre
  radioknappar (mall/enkel/arbetsmöte) plus en villkorlig flikväljare,
  bredare än standarddialogen (480px) för att etiketternas längre
  förklaringstexter inte ska radbrytas för tätt.
*/
#documentationExportDialog {
    width: min(560px, calc(100vw - 30px));
    display: none;
}

    #documentationExportDialog[open] {
        display: grid;
        gap: 13px;
        padding: 16px;
    }

.documentation-export-type {
    display: grid;
    gap: 10px;
    border: 1px solid #B6CBD8;
    border-radius: 8px;
    padding: 12px;
}

    .documentation-export-type legend {
        padding: 0 4px;
        color: #00375B;
        font-weight: 600;
    }

    /*
      display: block (INTE flex som tidigare, men INTE heller
      utelämnat helt) — användarens eget krav 2026-10: ingen indragen
      text när beskrivningen (Arbetsmöte-radens, den längsta) radbryts.
      Som flex-item börjar textens EGEN box först efter radioknappen,
      så en radbruten rad 2 hamnar kvar under rad 1:s textstart i
      stället för längst till vänster. Ett uttryckligt display: block
      krävs HÄR (i stället för att bara utelämna display helt) för att
      slå ut den betydligt mer generella ".property-form label, dialog
      label { display: grid; ...}"-regeln (se style.css, delad av bl.a.
      #documentationExportSplitLabel/#documentationExportSortLabel
      nedan, tänkt för en bildtext ovanför en meny) — annars staplas
      radioknappen OVANFÖR texten (en egen grid-rad var) i stället för
      bredvid den, eftersom "dialog label" annars vinner kampen om just
      display-egenskapen (ingen krock i övrigt: .documentation-export-
      type label har högre specificitet, men sätter inte display alls
      om inget uttryckligt värde anges här). Med display: block blir
      radioknappen och <span> i stället VANLIGA inline-element i normalt
      textflöde — <span>-texten radbryter då ända ut till label-radens
      egen vänsterkant, precis som normal brödtext, utan hängande
      indrag.
    */
    .documentation-export-type label {
        display: block;
        cursor: pointer;
    }

    .documentation-export-type input[type="radio"] {
        /*
          Samma bugg/fix som .ai-suggestion-detail-item input[type="checkbox"]
          nedan: den generella input/select/textarea-regeln (width: 100%)
          sträcker annars ut radioknappens osynliga layoutbox till nästan
          hela radens bredd, vilket tryckte in textspannet långt till
          höger med ett stort, tomt mellanrum.
        */
        width: auto;
        margin-right: 8px;
        vertical-align: top;
    }

/*
  Kalkylblad-/sorteringsmenyerna ska bara vara så breda som sin egen
  text/pilikon, inte sträckas ut till hela dialogens bredd — användarens
  eget krav 2026-10. Två steg krävs: dels menyn själv (width: auto
  skriver över input/select/textarea-basregelns width: 100% längre upp
  i filen), dels den OMSLUTANDE <label> (justify-self: start hindrar
  grid-cellen den själv upptar i #documentationExportDialog[open] ovan
  från att sträcka ETIKETTEN i sig till hela kolumnens bredd — annars
  skulle en auto-bred meny bara hamna mitt/till vänster i en ANDÅ bred,
  osynlig låda, utan synlig skillnad).
*/
#documentationExportSplitLabel,
#documentationExportSortLabel {
    justify-self: start;
}

#documentationExportSplitSelect,
#documentationExportSortSelect {
    width: auto;
}

/*
  Exportera-knappen längst till höger (i stället för .property-actions
  vanliga vänsterjustering) — användarens eget krav 2026-10, en egen
  tillkommande klass (inte ändring av basklassen .property-actions,
  som återanvänds av flera andra dialoger som INTE ska påverkas).
*/
.documentation-export-actions {
    justify-content: flex-end;
}

/*
  Riskmatris-dialogen (steg 5, se js/risk-matrix.js) — bredare än
  standarddialogen (480px) för att rymma 3×3-tabellen utan att klämma
  ihop cellernas text. Mycket innehåll (tabeller, urvalsmenyer, flikar)
  gör dialogen ovanligt hög, så den får samma storleksändrings-/
  flyttbarhet som #aiExportDialog redan har (se den egna kommentaren
  där för detaljer kring resize/makeDialogDraggable-mönstret, återanvänt
  rakt av här) — min-width/min-height är en nedre gräns man inte kan
  krympa dialogen under, så flikar/menyer/tabellens kolumner alltid
  har plats att rymmas.

  Öppnas med dialog.show() i stället för showModal() (se
  openRiskMatrixBtn i app.js) sedan 2026-10 — en punkt i matrisen går
  att klicka på för att markera/zooma till dess effektkedja i grafvyn
  (se wireRiskMatrixPointInteractions), vilket förutsätter att grafvyn
  går att SE medan dialogen är öppen, precis som ID-länkarna i
  Förklara-dialogen redan krävde (se #effectExplanationDialog-
  kommentaren för hela resonemanget om varför showModal()s overlay
  annars hindrar det här). Samma ersättning krävs därför: position:
  fixed + en egen startposition (övre VÄNSTRA hörnet — motsatt
  Förklara-dialogens övre HÖGRA, så de två inte hamnar exakt
  överlappande om båda råkar vara öppna samtidigt) i stället för
  webbläsarens automatiska centrering/top-layer-placering av :modal-
  dialoger, och ett uttryckligt z-index som matchar Förklaras.
*/
#riskMatrixDialog {
    width: min(900px, calc(100vw - 30px));
    min-width: 640px;
    /*
      height (INTE bara min-height) — ger dialogen en uttrycklig,
      fönsterbegränsad STARTSTORLEK (hela höjden ner till 20px från
      fönsterkanten, under top: 90px nedan) i stället för att låta
      den växa fritt efter innehållets naturliga höjd (ingen
      max-height finns längre, se [open]-regeln nedan) redan vid
      öppning. Utan detta skulle dialogen annars kunna starta högre
      än webbläsarfönstret på egen hand (inte bara via manuell
      dragning), vilket gör resize-handtaget i nedre högra hörnet
      oåtkomligt direkt från start — användarens eget fynd 2026-10.
      Ingen extra fast gräns (t.ex. 700px) ovanpå detta — så mycket av
      fönstrets höjd som faktiskt finns används, vilket minskar behovet
      av att skrolla sidopanelen i onödan på högre skärmar. resize:
      both (nedan) skriver över detta med ett eget, explicit
      höjdvärde så fort man drar i handtaget, precis som för width
      ovan — en startstorlek, ingen spärr mot att dra högre.
    */
    height: calc(100vh - 110px);
    min-height: 420px;
    display: none;
    resize: both;
    /*
      overflow: hidden (INTE auto som tidigare) sedan dialogen fick två
      lodräta sektioner (se .risk-matrix-body nedan) — huvudet
      (.dialog-header) ska aldrig behöva skrolla bort, bara de två
      sektionerna var för sig (.risk-matrix-sidebar/.risk-matrix-main,
      egna overflow-regler) ska göra det. hidden klipper i stället för
      att lägga till en egen, tredje skrollbar för HELA dialogen
      ovanpå de två inre — overflow måste ändå vara något annat än
      visible för att resize: both ska fungera alls, hidden funkar
      precis lika bra som auto för det syftet.
    */
    overflow: hidden;
    position: fixed;
    top: 90px;
    left: 20px;
    margin: 0;
    z-index: 500;
}

    /*
      INGEN max-height här (till skillnad från tidigare, och till
      skillnad från #effectExplanationDialog) — användarens eget krav
      2026-10: dialogen ska gå att dra hur hög som helst i nedre högra
      hörnet (resize: both ovan), bara min-height (420px, se
      #riskMatrixDialog ovan) sätter en nedre gräns. En dialog som görs
      högre än webbläsarfönstret klipps då inte av, men kan i gengäld
      hamna delvis utanför synligt område — ett medvetet val, inte en
      bugg, eftersom användaren uttryckligen bad om att slippa den
      övre gränsen VID MANUELL DRAGNING. Det är STARTSTORLEKEN (height
      ovan på #riskMatrixDialog, inte denna regel) som i stället ser
      till att dialogen aldrig startar större än fönstret av sig
      själv — de två kraven (ingen spärr vid dragning, men aldrig
      större än fönstret vid öppning) är alltså inte motsägande,
      bara lösta på två olika ställen.
    */
    #riskMatrixDialog[open] {
        display: flex;
        flex-direction: column;
        gap: 13px;
        padding: 16px;
    }

    #riskMatrixDialog .dialog-header {
        cursor: move;
        user-select: none;
    }

/*
  "Förklara"-dialogen (se js/effect-explanation.js) — kan bli lång
  (flera kedjor, varje med Mermaid-diagram), så samma storleksändrings-/
  flyttbarhet som #riskMatrixDialog/#aiExportDialog ovan, återanvänt
  rakt av (se #aiExportDialog-kommentaren för detaljer).

  Öppnas med dialog.show() i stället för showModal() (se
  openEffectExplanationDialog i app.js) — användarens eget krav
  2026-10: man ska kunna klicka/interagera med resten av sidan (framför
  allt grafvyn, för ID-länkarna i texten) medan dialogen är öppen, vilket
  showModal()s overlay annars hindrar. Det kostar två saker webbläsaren
  annars sköter GRATIS åt :modal-dialoger: ingen ::backdrop (redan bara
  avsett för modala dialoger, se regeln ovan) och ingen automatisk
  centrering/top-layer-placering — position: fixed + en egen startposition
  (övre högra hörnet, ur vägen för verktygsfältet) och ett uttryckligt
  z-index (högre än allt annat positionerat i appen, se t.ex.
  .graph-status) ersätter det. makeDialogDraggable (se app.js) läser av
  och skriver över denna startposition med explicit top/left först när
  man faktiskt börjar dra, precis som för de modala dialogerna.
*/
#effectExplanationDialog {
    width: min(760px, calc(100vw - 30px));
    min-width: 420px;
    min-height: 320px;
    display: none;
    resize: both;
    overflow: auto;
    position: fixed;
    top: 90px;
    /*
      left: auto krävs för att right faktiskt ska gälla — webbläsarens
      egen UA-stilmall sätter redan "left: 0" på <dialog> (icke-modal
      variant), och med BÅDE left, right OCH en uttrycklig width
      (ovan) blir positioneringen annars överbestämd: left vinner i
      LTR, och right ignoreras helt (dialogen hamnar då flush mot
      vänsterkanten i stället för uppe till höger).
    */
    left: auto;
    right: 20px;
    margin: 0;
    z-index: 500;
}

    #effectExplanationDialog[open] {
        display: flex;
        flex-direction: column;
        gap: 13px;
        padding: 16px;
        /*
          Ingen padding-top här (se padding-top på .dialog-header nedan
          i stället) — annars hamnar den sticky headern 16px NEDANFÖR
          dialogens egen övre kant, och den 16px-remsan ovanför den är
          fortfarande en del av samma scrollande yta (overflow: auto
          ligger på <dialog>-elementet, inte en egen inre behållare):
          skrollat innehåll som passerat förbi syns då rakt igenom den
          tomma remsan ovanför headern (användarens eget fynd 2026-10).
        */
        padding-top: 0;
        max-height: calc(100vh - 40px);
    }

    #effectExplanationDialog .dialog-header {
        cursor: move;
        user-select: none;
        /*
          Ingen h2 i den här headern (se kommentaren i index.html) —
          .dialog-headers justify-content: space-between skulle annars
          slänga den enda kvarvarande stäng-knappen till vänster, så
          den högerjusteras explicit här i stället.
        */
        justify-content: flex-end;
        /*
          Dialogen scrollar i sig SJÄLV (overflow: auto ovan ligger på
          <dialog>-elementet, inte på en egen inre content-behållare,
          se #effectExplanationDialog[open]) — utan position: sticky
          scrollar headern med resten av innehållet och försvinner helt
          ur synhåll så fort man skrollar nedåt (användarens eget fynd
          2026-10, INTE relaterat till att headern saknar en <h2>: en
          osynlig header går förstås inte att dra tag i längre heller).
          top: 0 gör att den fastnar precis vid dialogens padding-kant
          (0, se padding-top: 0 på [open] ovan), och en egen background
          täcker skrollat innehåll bakom stäng-knappen. Dialogens
          "vanliga" 16px-marginal överst flyttas hit som headerns EGEN
          padding-top i stället för att ligga på dialogen (se kommentaren
          där) — headerns bakgrund täcker då HELA den remsan också, så
          inget kan längre synas ovanför headern oavsett skrollposition.
        */
        position: sticky;
        top: 0;
        z-index: 1;
        background: #FFFFFF;
        padding-top: 16px;
    }

/*
  "Kopiera Markdown"-knappen längst ner i dialogen (se index.html) i
  stället för längst upp — samma högerjustering som riskmatrisens
  exportknappar (.risk-matrix-export-actions).
*/
.effect-explanation-actions {
    justify-content: flex-end;
}

/*
  Två lodräta sektioner i Riskmatris-dialogen (se index.html) —
  användarens eget krav 2026-10, efter att matrisen blivit väldigt
  hoptryckt i höjdled på mindre skärmar. .risk-matrix-sidebar (ALL
  text/menyer/kryssrutor/radioknappar/knappar) skrollar för sig om
  innehållet inte får plats i höjdled. .risk-matrix-main (ENBART
  matrisen) tar upp allt återstående utrymme. min-height: 0 på de
  flexade barnen krävs för att flexbox-barn med overflow ska kunna
  krympa UNDER sitt eget innehålls naturliga storlek — utan det skulle
  innehållet annars tvinga upp hela dialogens höjd i stället för att
  skrolla.

  Bredden mellan de två sektionerna är numera DRAGBAR via
  #riskMatrixSplitter (se wireRiskMatrixSplitterDrag i app.js, samma
  pointer-drag-mönster som appens egna leftSplitter/rightSplitter/
  aiSplitter/mermaidSplitter) i stället för en fast bredd — JS sätter
  .risk-matrix-sidebars width direkt (inline style), CSS-reglerna här
  ger bara ett FÖRVALT värde (200px) innan man dragit första gången.
  RISK_MATRIX_SIDEBAR_MIN_WIDTH/RISK_MATRIX_MAIN_MIN_WIDTH i app.js
  MÅSTE hållas i synk med min-width-värdena nedan — JS klampar
  dragningen mot samma gränser som CSS:en garanterar oavsett (ett
  dubbelt skyddsnät, inte bara JS, ifall sidopanelens width någonsin
  sätts på något annat sätt).
*/
.risk-matrix-body {
    display: flex;
    flex-direction: row;
    flex: 1 1 auto;
    min-height: 0;
}

.risk-matrix-sidebar {
    flex: 0 0 auto;
    width: 200px;
    min-width: 150px;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 12px;
    overflow-y: auto;
    overflow-x: hidden;
    min-height: 0;
}

/*
  #riskMatrixSplitter — en flex-ITEM här (till skillnad från appens
  egna splitters, som är EGNA GRID-SPÅR i huvudlayouten), behöver
  alltså en egen uttrycklig bredd i stället för att få den från en
  grid-template-columns-rad. 6px matchar appens övriga splitters
  (leftSplitter/rightSplitter/aiSplitter/mermaidSplitter).
  align-self: stretch gör att den fyller hela .risk-matrix-bodys höjd
  trots att den själva sidopanelen (align-items: flex-start) numera
  inte sträcker sina barn i höjdled.
*/
#riskMatrixSplitter {
    flex: 0 0 6px;
    align-self: stretch;
    margin: 0 4px;
}

.risk-matrix-main {
    flex: 1 1 auto;
    min-width: 300px;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

#riskMatrixTableContainer {
    flex: 1 1 auto;
    min-height: 0;
}

/*
  Knappar/menyer/etiketter i sidopanelen ska bara omfatta sin egen
  text (align-items: flex-start ovan på .risk-matrix-sidebar), inte
  sträckas ut till sidopanelens fulla bredd — användarens eget krav
  2026-10. De här tre undergrupperna (urvalsmenyerna, flikarna,
  exportknapparna) är VAR FÖR SIG också flex-behållare i kolumnriktning
  (för att stapla sina egna barn under varandra), så samma
  align-items: flex-start måste upprepas här — annars sträcks t.ex.
  de tre tidsperspektiv-knapparna ut till varandras bredd igen, fast
  en nivå längre ner, eftersom flexbox annars sträcker barn i
  tväraxeln som standard (även om den YTTRE behållaren inte gör det).
*/
.risk-matrix-scope-selects,
.risk-matrix-tabs,
.risk-matrix-export-actions {
    align-items: flex-start;
}

/*
  #riskMatrixChainFilterMode återanvänder den delade .filter-row-group
  (grid, se basregeln) — justify-items: start här (bara i Riskmatris-
  sidopanelen, INTE i basklassen som används på andra ställen i appen)
  av samma skäl som align-items: flex-start ovan: varje radioknapps
  rad ska bara omfatta sin egen text i bredd, inte sträckas ut till
  sidopanelens fulla bredd (grid sträcker barn i tväraxeln som
  standard, precis som flexbox).
*/
#riskMatrixChainFilterMode {
    justify-items: start;
}

/*
  Tidsperspektiv-flikarna (Nutid/RCP4,5/RCP8,5) under varandra i
  stället för sida vid sida — användarens eget krav 2026-10, sedan de
  flyttades till den smala sidopanelen. Ingen width: 100%/text-align
  längre (användarens eget krav 2026-10: knapparna ska bara omfatta
  sin egen text, som vanligt) — se align-items: flex-start ovan.
*/
.risk-matrix-tabs {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

    .risk-matrix-tabs button {
        padding: 6px 12px;
        border: 1px solid #B6CBD8;
        border-radius: 6px;
        background: #F6FBFD;
        color: #00375B;
        cursor: pointer;
    }

    .risk-matrix-tabs button[aria-selected="true"] {
        background: #00375B;
        color: #FFFFFF;
        border-color: #00375B;
    }

/*
  De tre kaskaderande urvalsmenyerna (System/Delsystem/Värde, se
  wireRiskMatrixScopeSelects i app.js) — under varandra (inte sida vid
  sida, användarens eget krav 2026-10 sedan de flyttades till den
  smala sidopanelen). Varje meny ska bara ta upp den bredd dess egna
  alternativ kräver, som vanligt (INGEN width: 100%, användarens eget
  krav 2026-10: knappar/menyer ska bara omfatta sin egen text) — är en
  alternativtext ändå för lång klipps den med en ellips i stället för
  att göra menyn bredare (max-width: 64ch ~ användarens egen gräns på
  64 tecken).
*/
.risk-matrix-scope-selects {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

    .risk-matrix-scope-selects label {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        gap: 3px;
        font-size: 12.5px;
        color: #00375B;
    }

    .risk-matrix-scope-selects select {
        max-width: 64ch;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

/*
  Riskmatris-tabellen (se buildRiskMatrixTableHtml i app.js) — en
  vanlig HTML-tabell i stället för en D3/SVG-ritning (se kommentaren
  vid riskMatrixDialog i index.html). Varje cells EGEN lista
  (.risk-matrix-cell-list) är det som faktiskt löser röran från
  SVG-varianten — radbryter naturligt OCH blir scrollbar för sig när
  den blir för hög, utan att svämma över i grannceller.

  table-layout: auto (INTE fixed) används medvetet, enligt användarens
  instruktion 2026-10: sannolikhetskolumnen (hörncellen +
  th[scope="row"]) ska bara vara så bred som dess text kräver — en
  fast bredd/procentandel går inte, eftersom textstorleken kan variera
  (teckenstorlek, zoom m.m.). Under table-layout: fixed avgörs
  kolumnbredder ENBART av de deklarerade bredderna (innehållet
  påverkar inte bredden alls, det bara svämmar över om det inte får
  plats) — ett tidigare försök att använda "width: 1% +
  white-space: nowrap" byggde på ett missförstånd av det klassiska
  CSS-knepet, som bara gäller table-layout: auto. Under auto räknar
  webbläsaren istället ut varje kolumns minsta nödvändiga
  innehållsbredd över ALLA rader (inte bara den första), så
  white-space: nowrap på sannolikhetskolumnen räcker för att den ska
  krympa till exakt rätt bredd. De tre konsekvenskolumnerna får en
  lika stor bredd-HINT (33%) var; eftersom deras innehåll radbryter
  (ingen nowrap) tvingar det inte upp bredden mot minsta
  innehållsbredd, så webbläsaren fördelar det kvarvarande utrymmet
  jämnt mellan dem enligt hinten istället.
*/
.risk-matrix-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 2px;
    table-layout: auto;
    font-size: 12.5px;
}

    .risk-matrix-table th {
        padding: 6px 4px;
        color: #00375B;
        font-weight: 600;
        text-align: center;
    }

    .risk-matrix-table .risk-matrix-corner {
        background: transparent;
        white-space: nowrap;
    }

    .risk-matrix-table th[scope="col"] {
        width: 33%;
    }

    .risk-matrix-table th[scope="row"] {
        padding: 4px 2px;
        white-space: nowrap;
    }

    /*
      Lodrät text (Figur 10, sid 40 i metodstödet) för BÅDE den egna
      "Sannolikhet"-axelrubriken OCH Sannolikhet-nivåerna (Ofta/Ibland/
      Sällan) — gäller bara den HÄR, RIKTIGA Riskmatris-dialogen.
      Motsvarande mini-riskmatris i Förklara-dialogen (se
      buildMiniRiskMatrixHtml i effect-explanation.js) är MEDVETET
      annorlunda och oförändrad: där är nivåerna vågräta och bara
      axelrubriken lodrät — de två riskmatriserna ser med flit olika ut
      (användarens eget krav 2026-10). writing-mode vänder texten 90°,
      rotate(180deg) vänder den ytterligare ett halvt varv så att den
      läses underifrån och upp (samma konvention som en diagramaxels
      rubrik).

      Stilen ligger på den INRE <span>:en, INTE på själva <th>-cellen —
      en <th> med writing-mode: vertical-rl på sig själv gör att
      cellens EGEN vertical-align: middle (särskilt viktig för
      "Sannolikhet", som spänner tre rader via rowspan) tolkas om efter
      den nya, lodräta skrivriktningen i stället för tabellradens —
      texten hamnade i praktiken längst ned i cellen i stället för
      centrerad (upptäckt 2026-10). Med writing-mode/rotate bara på
      spannet förblir <th> kvar i vanlig vågrät skrivriktning, så dess
      vertical-align centrerar spannet som vanligt; display: inline-
      block på spannet krävs för att transform/writing-mode ska få
      effekt på ett annars inline-element.
    */
    .risk-matrix-level-label span,
    .risk-matrix-axis-label-vertical span {
        writing-mode: vertical-rl;
        transform: rotate(180deg);
        display: inline-block;
    }

    .risk-matrix-level-label,
    .risk-matrix-axis-label-vertical {
        text-align: center;
        vertical-align: middle;
    }

    .risk-matrix-axis-label-vertical {
        width: 1.4em;
    }

    .risk-matrix-axis-label {
        text-align: center;
    }

.risk-matrix-cell {
    vertical-align: top;
    border-radius: 6px;
    padding: 6px;
}

.risk-matrix-cell-header {
    font-weight: 700;
    font-size: 11.5px;
    color: #222222;
    margin-bottom: 4px;
}

.risk-matrix-cell-list {
    margin: 0;
    padding-left: 16px;
    max-height: 220px;
    overflow-y: auto;
    color: #111111;
}

    .risk-matrix-cell-list li {
        padding: 1px 0;
        overflow-wrap: anywhere;
    }

    .risk-matrix-cell-list .risk-matrix-cell-empty {
        list-style: none;
        padding-left: -16px;
        margin-left: -16px;
        color: #666666;
    }

/*
  En punkt i punktlistan (en Effekt–Värde-kombination, se cellHtml i
  buildRiskMatrixTableHtml) — klickbar OCH hovringsbar (se
  wireRiskMatrixPointInteractions i app.js), underlinjerad prickat
  precis som bedömningsvärdena med tooltip i Förklara-dialogen (se
  .effect-explanation-value-tooltip), samma visuella signal "det finns
  mer information här" återanvänd. Användarens eget krav 2026-10.
*/
.risk-matrix-point {
    cursor: pointer;
    text-decoration: underline dotted;
    text-decoration-color: currentColor;
    text-underline-offset: 2px;
}

/*
  Riskmatrisens punkt-tooltip (se #riskMatrixPointTooltip i index.html
  och wireRiskMatrixPointInteractions i app.js) — position: fixed och
  ETT delat element, flyttat/fyllt i JS vid hovring, i stället för en
  vanlig title-tooltip (kan bara visa text, inte ett renderat Mermaid-
  SVG-diagram). z-index högre än både dialogen (500, se
  #riskMatrixDialog) och grafytans egna statusindikatorer, så den
  alltid hamnar överst. pointer-events: none — tooltipen ska aldrig
  själv fånga musen (då skulle mouseleave på punkten den täcker aldrig
  hinna hända, och tooltipen bli kvar hängande).
*/
.risk-matrix-point-tooltip {
    position: fixed;
    z-index: 600;
    max-width: min(420px, calc(100vw - 28px));
    background: #FFFFFF;
    border: 1px solid #B6CBD8;
    border-radius: 8px;
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
    padding: 10px 12px;
    pointer-events: none;
}

    .risk-matrix-point-tooltip-header {
        font-weight: 700;
        font-size: 12.5px;
        color: #00375B;
        margin-bottom: 6px;
    }

    .risk-matrix-point-tooltip-diagram {
        font-size: 12px;
        color: #555555;
    }

    .risk-matrix-point-tooltip-diagram svg {
        max-width: 100%;
        height: auto;
    }

#riskMatrixTableContainer {
    overflow-x: auto;
}

    #aiSuggestionDetailsDialog[open] {
        display: grid;
        gap: 13px;
        padding: 16px;
        max-height: calc(100vh - 40px);
        overflow-y: auto;
        /*
          Säkerhetsnät mot ETT långt, ordelningsfritt ord/värde (t.ex.
          ett långt egenskapsvärde eller nodnamn utan mellanslag) någon
          enstaka gång i FRAMTIDEN tvingar raden bredare än dialogen —
          bryter i så fall mitt i ordet i stället för att svämma över
          kanten. Den FAKTISKA bredbuggen (kryssrutans width: 100%, se
          .ai-suggestion-detail-item nedan) är fixad separat; det här är
          bara ett generellt skydd för resten av dialogens text.
        */
        overflow-wrap: anywhere;
    }

.ai-suggestion-detail-rationale {
    margin: 0;
    color: var(--muted);
    font-size: 13px;
    white-space: pre-line;
}

.ai-suggestion-detail-section h3 {
    margin: 0 0 4px;
    color: #00375B;
    font-size: 13px;
}

.ai-suggestion-detail-section ul {
    margin: 0;
    padding-left: 18px;
    font-size: 13px;
}

.ai-suggestion-detail-section li {
    margin-bottom: 4px;
}

.ai-suggestion-detail-section li ul {
    margin-top: 2px;
    font-size: 12px;
    color: var(--muted);
}

/*
  Kryssrutan + innehållet på en och samma rad, hela raden klickbar
  (label omsluter båda) — se renderAiSuggestionDetailsBody/
  renderCheckableItem i app.js.
*/
.ai-suggestion-detail-item {
    display: flex;
    align-items: flex-start;
    gap: 7px;
    cursor: pointer;
}

    .ai-suggestion-detail-item input[type="checkbox"] {
        /*
          Återställer den generella input/select/textarea-regeln
          (width: 100% + border/padding/border-radius för textfält, se
          högre upp i filen) — utan detta blir kryssrutans osynliga
          layoutbox nästan lika bred som HELA raden (eftersom width:
          100% räknas mot flex-radens tillgängliga bredd), vilket
          klämmer in textspannet bredvid i en smal rest-kolumn och fick
          texten att svämma över dialogens högra kant, med ett stort,
          till synes tomt utrymme mellan kryssrutan och texten som följd.
        */
        width: auto;
        margin-top: 3px;
        padding: 0;
        border: none;
        flex-shrink: 0;
        accent-color: #00375B;
    }

    .ai-suggestion-detail-item > span {
        /*
          Åsidosätter flex-items annars implicita min-width: auto (som
          skulle hindra spannet från att krympa under sitt INNEHÅLLS
          naturliga bredd) — annars kan en lång, ordelningsfri sträng
          ändå tvinga raden bredare än dialogen, trots att kryssrutan
          ovan nu har en fast, liten bredd. overflow-wrap: anywhere ärvs
          redan ner hit från #aiSuggestionDetailsDialog[open] ovan.
        */
        min-width: 0;
    }

/*
  Avbockad = kommer INTE tillämpas vid Implementera — genomstruken och
  nedtonad, samma "det här är inaktuellt"-signal som .ai-suggestion-
  row-invalid använder på en helt annan nivå (en hel RAD i listan, inte
  en enskild detalj här).
*/
.ai-suggestion-detail-excluded {
    text-decoration: line-through;
    color: var(--muted);
    opacity: 0.7;
}

/*
  Autosave-etiketten i grafytans övre högra hörn är klickbar (öppnar
  versionsdialogen), och ska se ut som det. Understrykningen visas bara
  vid hovring, så etiketten i vila fortfarande läses som status och
  inte som en knapp.
*/
.graph-status-clickable {
    cursor: pointer;
}

    .graph-status-clickable:hover {
        color: #00375B;
        text-decoration: underline;
    }

@media (max-width: 1000px) {
    .toolbar {
        gap: 9px;
    }

    .app-layout {
        grid-template-columns: 195px minmax(280px, 1fr) 270px;
    }
}

@media (max-width: 760px) {
    /*
      Mobilläget är det ENDA stället sidan FAKTISKT ska få scrolla som
      helhet (se motiveringen vid html/body ovan): paneler/grafyta/
      Mermaid-panel staplas i stället för att dela en gemensam
      skärmhöjd, och då måste sidan tillåtas växa och scrolla för att
      allt staplat innehåll ska gå att nå.
    */
    html,
    body {
        height: auto;
        overflow: visible;
    }

    .toolbar {
        align-items: flex-start;
        flex-wrap: wrap;
    }

    .toolbar-right {
        margin-left: 0;
    }

    .app-layout {
        height: auto;
        min-height: 0;
        display: block;
        overflow: visible;
    }

    .splitter {
        display: none;
    }

    .left-panel,
    .right-panel {
        max-height: 300px;
        border: 0;
        border-bottom: 1px solid var(--line);
    }

    .graph-area {
        height: 480px;
    }

    .mermaid-panel {
        height: 300px;
    }

    .graph-footer {
        align-items: flex-start;
        flex-direction: column;
    }
}

/*
  Preset-väljaren (se js/preset-gate.js) — numera en VANLIG <dialog>
  (se motiveringen i index.html), så den ärver den generella
  dialog-regeln (border/bakgrund/skugga/::backdrop, se den regeln högt
  upp i denna fil) och behöver bara sin egen bredd/padding/maxhöjd här
  — "Skapa ny preset"-vyn har en 280px hög textarea och mår bättre av
  lite mer bredd (640px) än appens övriga, smalare dialoger (480px).
*/
.preset-gate-overlay {
    width: min(640px, calc(100vw - 30px));
    max-height: calc(100vh - 60px);
    padding: 1.5rem 2rem;
    overflow-y: auto;
}

.preset-gate-overlay h1 {
    font-size: 1.25rem;
    margin-top: 0;
}

.preset-gate-muted {
    color: #666;
    font-size: 0.9rem;
}

.preset-gate-screen[hidden] {
    display: none;
}

.preset-gate-textarea {
    width: 100%;
    height: 280px;
    font-family: ui-monospace, monospace;
    font-size: 0.85rem;
}

.preset-gate-error {
    background: #fdecea;
    border: 1px solid #f5c2c0;
    color: #8a1f17;
    padding: 0.6rem;
    margin: 0.5rem 0;
    white-space: pre-wrap;
}

.preset-gate-error[hidden] {
    display: none;
}

.preset-gate-success {
    background: #e9f6ec;
    border: 1px solid #b7e0c0;
    color: #1f6b33;
    padding: 0.6rem;
    margin: 0.5rem 0;
}

#presetGateBuiltins {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin: 0.5rem 0;
}
