/* ===========================================================================
   ostkind.social - eigenes CSS
   ---------------------------------------------------------------------------
   Quelle (HIER bearbeiten):  /opt/mastodon/branding/custom.css
   Aktiv über:                Admin -> Servereinstellungen -> Erscheinungsbild
                              (Feld "Eigenes CSS", entspricht Setting.custom_css)
   Stand:                     2026-08-02

   ENTWURF: clean, flach, ohne Zierrat. Dunkel orientiert an X "Lights out",
   der Grund aber leicht angehoben (#0A0A0B statt reinem Schwarz), das nimmt
   die Härte, ohne den Look zu verlieren. Hell ist die saubere Weiß-Variante.
   Keine Verläufe, keine Schatten, keine Bewegung außer einem Farbwechsel.
   Struktur trägt Linie und Abstand, nicht Dekoration.

   Nach dem Bearbeiten der Datei neu einspielen:
     cd /opt/mastodon
     docker compose cp branding/custom.css web:/tmp/custom.css
     docker compose exec -T web bin/rails runner \
       'Setting.custom_css = File.read("/tmp/custom.css")'
     docker compose exec -T web bin/rails runner \
       'Rails.cache.delete(:setting_digest_custom_css)'

   Komplett zurücknehmen (wirkt sofort, kein Neustart nötig):
     docker compose exec -T web bin/rails runner \
       'Setting.custom_css = ""; Rails.cache.delete(:setting_digest_custom_css)'

   Die zweite Zeile ist NICHT optional: theme_helper.rb hält den SHA256 des CSS
   im Cache und baut daraus den Dateinamen. Ohne die Invalidierung verweist die
   Seite weiter auf den alten Hash.

   AUFBAU: Abschnitt 1 und 2 fassen nur Design-Tokens an und sind upgrade-fest.
   Ab Abschnitt 3 stehen Klassennamen. Fällt einer bei einem Upgrade weg,
   verschwindet nur dieser Effekt, es bricht nichts.
   =========================================================================== */


/* --- 1. Palette ----------------------------------------------------------
   Mastodon 4.6 leitet jede Farbe aus zwei Rohrampen ab (theme/_base.scss):
   Indigo trägt die Marke, Grau trägt Grund, Text und Linien. Hier ansetzen
   färbt die komplette Oberfläche um, hell wie dunkel.                       */

html {
  /* 1a) Marke: aus dem Branding-Blurple #6364FF in OKLCH erzeugt. Je Stufe
     die Helligkeit der Originalrampe behalten, nur Farbton und Sättigung
     getauscht, deshalb bleiben alle Kontraste erhalten. */
  --color-indigo-50: #eef1ff;
  --color-indigo-100: #dee3ff;
  --color-indigo-200: #c4ceff;
  --color-indigo-300: #9facff;
  --color-indigo-400: #7780ff;
  --color-indigo-500: #6364ff;  /* exakt die Branding-Farbe */
  --color-indigo-600: #5246f4;
  --color-indigo-700: #4638d9;
  --color-indigo-800: #3937a6;
  --color-indigo-900: #313384;
  --color-indigo-950: #1d204d;

  /* 1b) Neutralrampe nach dem Vorbild von X: kühl, entsättigt, an den Enden
     weit auseinander. Mastodons Standardgrau hat einen violetten Unterton
     (#67678e, #181820), der die Oberfläche weich und bunt wirken lässt.
     Diese Rampe ist praktisch farblos, dadurch der nüchterne Eindruck. */
  --color-grey-50: #f7f9f9;
  --color-grey-100: #eff1f3;
  --color-grey-200: #e3e6e9;   /* Linien im hellen Modus */
  --color-grey-300: #c9cdd2;
  --color-grey-400: #9ba1a8;
  --color-grey-500: #82858c;
  --color-grey-600: #536471;   /* Nebentext hell, X-Wert */
  --color-grey-700: #3d4046;
  --color-grey-800: #2e3035;   /* Linien im dunklen Modus, X-Wert */
  --color-grey-900: #17181c;   /* Karten und erhöhte Flächen, X-Wert */
  --color-grey-950: #0a0a0b;   /* Seitengrund */

  /* Runde Avatare. Standard sind 8px. Der Kreis ist das auffälligste
     Einzelmerkmal von X und kostet eine Zeile. */
  --avatar-border-radius: 50%;

  /* Dünne, farblose Rollbalken. */
  scrollbar-color: var(--color-grey-700) transparent;
  scrollbar-width: thin;
}


/* --- 2. Semantische Tokens je Modus --------------------------------------
   Die Selektoren sind wörtlich die aus theme/index.scss, damit die Spezifität
   passt. Gewinnen tut diese Datei, weil sie zuletzt geladen wird.

   Der Hochkontrast-Modus bleibt unangetastet: dessen Regeln tragen
   [data-contrast='high'] zusätzlich im Selektor und sind damit spezifischer. */

[data-color-scheme='dark'],
html:not([data-color-scheme]) {
  --color-text-primary: #e7e9ea;                 /* auf Grund 16,3:1 */
  --color-text-secondary: var(--color-grey-500); /* auf Grund 5,4:1, auf Karte 4,8:1 */
  --color-text-tertiary: var(--color-grey-500);

  /* X selbst benutzt #71767B für Nebentext. Das ergibt auf erhöhten Flächen
     nur 3,95:1 und reißt damit die Lesbarkeitsnorm (AA verlangt 4,5). Die
     Rampe liegt deshalb acht Punkte heller. Optisch ist der Unterschied
     kaum zu sehen, an der Norm entscheidet er. */
}

[data-color-scheme='light'] {
  --color-text-primary: #0f1419;  /* auf Weiß 18,5:1, X-Wert */
}


/* --- 3. Flächen flach legen ----------------------------------------------
   Mastodon rundet Spalten und Spaltenköpfe mit 4px an. Das wirkt bei
   randlosen Zeitleisten wie ein halber Kasten. X ist an dieser Stelle
   kantig: durchgehende Fläche, Trennung nur über Haarlinien.                */

.column-header,
.column-header__wrapper,
.column > .scrollable {
  border-radius: 0;
}


/* --- 4. Luft -------------------------------------------------------------
   glitch setzt die Beitragspolsterung bewusst herunter (10px 14px). Für den
   ruhigen Eindruck ist Abstand aber das wirksamste Mittel überhaupt, mehr
   als jede Farbe. X liegt bei 12px 16px, hier etwas großzügiger.            */

.status {
  padding: 14px 16px;
}


/* --- 5. Knöpfe -----------------------------------------------------------
   Pille, halbfett, ein reiner Farbwechsel beim Überfahren. Kein Anheben,
   kein Schein, keine Skalierung.                                            */

.button {
  border-radius: 999px;
  font-weight: 600;
  transition: background-color 0.15s ease;
}


/* --- 6. Kleinkram --------------------------------------------------------
   Textauswahl in Markenfarbe, und Code aus Markdown-Beiträgen (glitch kann
   das) auf einer ruhigen Fläche statt nackter Monospace. Fokusrahmen sind
   bewusst NICHT angefasst: Mastodon 4.6 bringt dafür bereits
   --outline-focus-default mit, das greift die Markenfarbe von allein ab.    */

::selection {
  background: color-mix(in oklab, var(--color-indigo-500) 30%, transparent);
}

.status__content code {
  background: var(--color-bg-secondary);
  border-radius: 4px;
  padding: 0.05em 0.35em;
  font-size: 0.92em;
}

.status__content pre code {
  display: block;
  padding: 0.7em 0.9em;
  overflow-x: auto;
}


/* --- 7. Schreibfeld als Fenster in der Mitte (EXPERIMENT) ----------------
   Mastodon 4.6 kennt kein zentriertes Schreibfenster. In der Modal-Liste steht
   nur COMPOSE_PRIVACY, kein COMPOSE. Das Feld hängt fest in der linken Spalte
   (`.compose-panel`), und beim Antworten wird lediglich das Textfeld fokussiert.

   Dieser Block täuscht das Verhalten von X und Bluesky mit reinem CSS vor:
   sobald das Schreibfeld den Fokus hat, springt es in die Bildschirmmitte, mit
   Abdunkelung dahinter. Klick daneben nimmt den Fokus weg, es geht zurück.
   Beim Antworten greift es ebenfalls, weil Mastodon dabei fokussiert.

   >>> Das ist eine Krücke, keine Lösung. Siehe "Bekannte Lücken" unten. <<<

   ---------------------------------------------------------------------------
   DER FALLSTRICK, DER DEN ERSTEN ANLAUF GEKOSTET HAT

   `position: fixed` erzeugt IMMER einen eigenen Stapelkontext, auch ohne
   z-index. Der Behälter der linken Spalte, `.columns-area__panels__pane__inner`,
   ist fix positioniert. Ein z-index am Schreibfeld wirkt damit nur INNERHALB
   dieses Behälters, und der Behälter selbst liegt auf der Ebene `auto`, also
   unter jedem Element mit positivem z-index in der Wurzel. Der Schleier am
   body lag dadurch ÜBER dem Fenster.

   Sichtbar war davon nichts: im dunklen Modus liegt ein fast schwarzes Fenster
   unter einem zu 55 % schwarzen Schleier auf fast schwarzem Grund. Es sah aus,
   als würde das Textfeld verschwinden, dabei war es die ganze Zeit da.

   Deshalb wird hier der BEHÄLTER angehoben, nicht das Feld. Alles Weitere
   passiert dann innerhalb seines Kontextes.
   ---------------------------------------------------------------------------

   Zentriert wird NUR `.compose-form`, nicht `.compose-panel`. Im Panel stecken
   auch Suchfeld und Link-Fußzeile. Weil der angehobene Behälter beide mit über
   den Schleier zieht, werden sie währenddessen ausgeblendet.

   Unter 1175px greift der Block absichtlich nicht: dort rendert Mastodon das
   Panel gar nicht erst (`useBreakpoint('full')`), und `ensureComposeIsVisible()`
   schickt einen ohnehin auf die Seite `/publish`. Das ist das bessere Verhalten,
   es soll nicht überschrieben werden.

   Kein `transform` zum Zentrieren, sondern `inset: 0` mit `margin: auto`.
   Ein transform würde ebenfalls einen Containing Block aufmachen und alles
   darin Positionierte ans Fenster binden statt an den Bildschirm.

   z-index 3000 liegt über der Oberfläche (die bleibt unter 2000), aber unter
   Mastodons eigener Modalebene (9998/9999). Emoji-Auswahl und
   Sichtbarkeitsdialog liegen dadurch weiterhin obenauf und nicht darunter.

   Die drei Auslöser stehen als Liste in einem einzigen `:has()`. Das ist
   erlaubt und spart es, jede Regel dreimal zu schreiben.                      */

@media (min-width: 1175px) {
  /* 7a) Den fix positionierten Behälter der linken Spalte über den Schleier
     heben. Ohne diese Regel bleibt alles Weitere unsichtbar. */
  body:has(
      .compose-panel .compose-form:focus-within,
      .emoji-picker-dropdown__menu,
      .visibility-modal
    )
    .columns-area__panels__pane--compositional
    .columns-area__panels__pane__inner {
    z-index: 3000;
  }

  /* 7b) Das Schreibfeld als Fenster in die Bildschirmmitte. */
  body:has(
      .compose-panel .compose-form:focus-within,
      .emoji-picker-dropdown__menu,
      .visibility-modal
    )
    .compose-panel
    .compose-form {
    position: fixed;
    /* Oben angeschlagen, nicht mittig. X und Bluesky setzen das Fenster
       deutlich höher als die Bildschirmmitte, dort erwartet es das Auge.
       `clamp` hält den Abstand auf sehr flachen wie sehr hohen Bildschirmen
       in einem brauchbaren Bereich.
       Waagerecht zentriert über `inset-inline` statt `left`/`right`, damit
       es in RTL-Sprachen genauso sitzt (Mastodon bringt rtl.scss mit). */
    top: clamp(24px, 7vh, 88px);
    inset-inline: 0;
    margin-inline: auto;
    z-index: 1;
    width: min(600px, calc(100vw - 48px));
    height: fit-content;
    max-height: min(78vh, 700px);
    overflow-y: auto;
    padding: 16px;
    background: var(--color-bg-primary);
    /* Bewusst eine Stufe heller als die übrigen Linien: gegen den abgedunkelten
       Grund ist der Rahmen die einzige Kante, die das Fenster abgrenzt. */
    border: 1px solid var(--color-grey-700);
    border-radius: 16px;
    box-shadow: 0 24px 64px rgb(0 0 0 / 55%);
  }

  /* 7c) Suchfeld und Fußzeile verstecken, solange das Fenster offen ist. Sie
     hängen im selben angehobenen Behälter und lägen sonst als einzige Reste
     der Seitenspalte unabgedunkelt über dem Schleier. */
  body:has(
      .compose-panel .compose-form:focus-within,
      .emoji-picker-dropdown__menu,
      .visibility-modal
    )
    .compose-panel
    > :not(.compose-form) {
    opacity: 0;
    pointer-events: none;
  }

  /* 7d) Abdunkelung als ::after am body, nicht als ::before am Fenster selbst.
     Ein negativer z-index innerhalb des Fensters würde nach den Malregeln
     dessen eigenen Hintergrund mit abdunkeln, nicht nur die Seite dahinter.
     Der Schleier fängt Klicks ab, und genau das schließt das Fenster: der
     Klick nimmt dem Textfeld den Fokus. */
  body:has(
      .compose-panel .compose-form:focus-within,
      .emoji-picker-dropdown__menu,
      .visibility-modal
    )::after {
    content: '';
    position: fixed;
    inset: 0;
    z-index: 2999;
    background: rgb(0 0 0 / 55%);
  }
}

/* Bekannte Lücken, bewusst nicht geflickt:

   1. Emoji-Auswahl und Sichtbarkeitsdialog rendern über Portale AUSSERHALB des
      Schreibfelds (react-overlays mit `strategy: fixed`, die Sichtbarkeit sogar
      als eigenes Modal). Damit ist `:focus-within` weg und das Fenster würde
      zurückspringen. Die beiden Zusatzeinträge in der `:has()`-Liste fangen
      genau diese zwei Fälle ab.
   2. Andere Aufklapper im Schreibfeld (Sprache, Umfrage, bei glitch der
      Inhaltstyp) sind NICHT abgefangen. Sie benutzen die allgemeine Klasse
      `.dropdown-menu`, und die auszuwerten würde das Fenster auch bei jedem
      unbeteiligten Menü irgendwo auf der Seite aufziehen. Beim Öffnen eines
      solchen Menüs klappt das Fenster also kurz in die Seitenspalte zurück.
   3. `.visibility-modal` in der Liste greift auch, wenn der Sichtbarkeitsdialog
      aus dem Kontextmenü eines fremden Beitrags kommt. Dann zentriert sich das
      leere Schreibfeld ohne Anlass.
   4. Ein angefangener Entwurf verschwindet beim Wegklicken zurück in die
      Seitenspalte, statt wie bei X das Fenster offen zu halten. Ohne
      JavaScript gibt es keinen Zustand, an dem sich das festmachen ließe.

   Nach jedem Mastodon-Upgrade prüfen: ändern sich `.compose-panel`,
   `.compose-form`, `.columns-area__panels__pane__inner`,
   `.emoji-picker-dropdown__menu` oder `.visibility-modal`, fällt der Block
   still aus. Er bricht nichts, das Feld klebt dann wieder links wie vorher. */


