%<*driver>
\def\nameofplainTeX{plain}
\ifx\fmtname\nameofplainTeX
\input l3docstrip.tex
\keepsilent
\askforoverwritefalse
\preamble

========================================================================
 osgstyler - 

 (C) 2026 Matthias Werner
 E-mail: matthias.werner@informatik.tu-chemnitz.de

 Released under the LaTeX Project Public License v1.3c or later
 See http://www.latex-project.org/lppl.txt
======================================================================== 

\endpreamble
\postamble

 This work is "maintained" (as per LPPL maintenance status) by
 Matthias Werner.
\endpostamble

\generate{
  \file{osgstyler.sty}{\from{osgstyler.dtx}{pkg}}
  \file{osgstyler-lua.sty}{\from{osgstyler.dtx}{lua-backend}}
  }
  \expandafter\endbatchfile
\fi

\iffalse
%</driver>
%<*pkg>
\NeedsTeXFormat{LaTeX2e}[2024-06-01]
\ProvidesExplPackage{osgstyler}
{2026-08-24}
{v0.2.0}
{Template-based, reusable document styles}
%</pkg>
%<*driver>
\fi

\documentclass[add-bib=false]{osgdoc}

\usepackage{enumitem}
\usepackage{microtype}
\usepackage[
languages={de,en},
map={de=german,en=english},
targetlang={job=2},
load babel
]{langselect}

\newpackagename{\osgstyler}{osgstyler}
\newpackagename{\lttalk}{ltx-talk}
\newpackagename{\beamer}{beamer}

\makeindex
\setcnltx{
  name  =  osgstyler,
  version  = \UseVersionOf{osgstyler.sty},
  date     = \UseDateOf{osgstyler.sty},
  title    = {\ldeen{Das \osgstyler\ Paket}{The \osgstyler\ Package}},
  info     = {\ldeen{Templatebasierte Stile für Standardformatierungen}{
    Template-based styles for standard formattings}},
  authors  = {Matthias Werner[matthias.werner@informatik.tu-chemnitz.de]},
  abstract={
  \ldeen{
      \osgstyler{} verwaltet Farben, Schriften, Abstände, Listenoptik,
      Dekorationen und kastenartige Umgebungen (Zitate, theorem-artige
      Blöcke, \ldots) über ein kleines, portables Namensvokabular, statt
      konkrete Werte im Dokument zu verstreuen. Mitgelieferte Presets
      (\code{osl-standard}, \code{osl-modern}) geben ein sofort nutzbares
      Erscheinungsbild; eigene Paletten, Dekorationen und Blockinstanzen
      lassen sich darüber hinaus deklarieren.

      Das Paket ist Teil des \pkg*{osglecture}-Bundles -- \cls*{ltx-talk}
      teilt sein Farb-Slot-Vokabular mit \pkg*{lttheme} --, kann aber auch
      unabhängig eingesetzt werden.
    }{
      \osgstyler{} manages colors, fonts, spacing, list appearance,
      decorations, and box-like environments (quotations, theorem-like
      blocks, \ldots) through a small, portable vocabulary of names, instead
      of scattering concrete values across a document. Bundled presets
      (\code{osl-standard}, \code{osl-modern}) give an immediately usable
      appearance; custom palettes, decorations, and block instances can be
      declared on top of that.

      The package is part of the \pkg*{osglecture} bundle -- \cls*{ltx-talk}
      shares its color-slot vocabulary with \pkg*{lttheme} --, but can also
      be used independently.
    }
  },
  url      =https://github.com/werner-matthias/osglecture,
  build-title
}
\begin{document}
% --------------------------------------------------------------------------
% Documentation
% --------------------------------------------------------------------------

\section{\ldeen{Einleitung}{Introduction}}
\label{sec:intro}

\subsection{TL;DR}
\label{sec:tldr}
\ldeen{Angenommen, Sie wollen Ihrem Dokument schnell ein in sich stimmiges
  Erscheinungsbild geben -- koordinierte Farben, eine passende Begleitschrift
  und etwas Optik für Zitate --, ohne sich mit den Details der
  Farb-/Font-/Box-Verwaltung zu beschäftigen.}{Assume you want to quickly give
  your document a coherent look -- coordinated colors, a matching companion
  typeface, and some visual treatment for quotations -- without dealing with
  the details of color/font/box management yourself.}
\begin{enumerate}
  \item \ldeen*{Binden Sie @1 ein}{Include @1}
    {\cs{usepackage}\Marg{osgstyler}}
  \item \ldeen*{Aktivieren Sie ein fertiges Preset mit @1}{Activate a
    ready-made preset with @1}{\cs{UseOsgPreset}\Marg{osl-modern}}
  \item \ldeen{Falls Sie auch Zitate/Blöcke im Langform-Stil nutzen wollen,
    aktivieren Sie diese einmalig mit}{If you also want long-form-style
    quotations/blocks, activate them once with}:

    \centerline{\cs{UseOsgStandardBlockEnvironments}}
\end{enumerate}
\begin{example}[code-only]
    \usepackage{osgstyler}
    \UseOsgPreset{osl-modern}
    \UseOsgStandardBlockEnvironments

    \begin{document}
    \emph{Hervorhebungen}, \textbf{Fettes} und \highlight{Markiertes}
    nutzen jetzt automatisch die Preset-Farben.

    \begin{quote}
      Auch Zitate erhalten das Erscheinungsbild des Presets.
    \end{quote}
    \end{document}
\end{example}
\ldeen*{Das war schon alles auf dieser Ebene. Alles Weitere in dieser
  Dokumentation -- eigene Paletten, eigene Blockinstanzen, eigene
  Dekorationen -- brauchen Sie nur, wenn Sie über die mitgelieferten
  Presets hinaus wollen. Der Unterschied zwischen diesen zwei Ebenen
  -- \emph{Nutzen} vorgefertigter Stile gegenüber \emph{Erstellen}
  eigener -- zieht sich durch die gesamte Dokumentation und wird in
  Abschnitt~@1 genauer erklärt.}{That's already everything at this
  level. Everything else in this documentation -- your own palettes,
  your own block instances, your own decorations -- is only needed if
  you want to go beyond the bundled presets. The distinction between
  these two levels -- \emph{using} ready-made styles versus
  \emph{creating} your own -- runs through this entire documentation
  and is explained in detail in Section~@1.}{\ref{sec:concepts}}

\subsection{\ldeen{Motivation}{Motivation}}
\label{sec:motivation}
\ldeen{Wiederkehrende Lehr- oder Vortragsmaterialien -- Skripte, Folien,
  Handouts -- sollen meist ein einheitliches, wiedererkennbares
  Erscheinungsbild haben: dieselben Farben, dieselben Schriften, dieselbe
  Optik für Zitate, Hervorhebungen und theorem-artige Blöcke. Ohne
  Unterstützung landet dieses Wissen verstreut in jedem einzelnen Dokument --
  als wiederholte \cs*{definecolor}-Aufrufe, hartcodierte \cs*{fcolorbox}s
  und \cs*{textcolor}-Aufrufe --, und eine spätere Änderung (ein neues
  Farbschema, eine andere Begleitschrift) bedeutet, jede Fundstelle einzeln
  zu suchen.

  \pkg*{osgstyler} löst das, indem es visuelle Ressourcen (Farben, Schriften,
  Abstände, Listenoptik, Dekorationen, Kastenoptik) hinter einem kleinen,
  \emph{portablen} Namensvokabular versteckt: Dokumentcode verweist auf
  \emph{Slots} wie \code{primary} oder \code{accent}, nie auf konkrete Werte.
  Ein Theme oder eine Palette legt einmalig fest, was sich hinter diesen
  Namen verbirgt; eine Änderung dort wirkt überall.

  Das Paket ist Teil des \pkg*{osglecture}-Bundles -- \cls*{ltx-talk} nutzt
  denselben Slot-Wortschatz für Farben wie \pkg*{lttheme} --, kann aber auch
  vollständig unabhängig eingesetzt werden.}{Recurring teaching or
  presentation material -- lecture notes, slides, handouts -- usually needs a
  consistent, recognizable look: the same colors, the same fonts, the same
  visual treatment for quotations, highlights, and theorem-like blocks.
  Without support, this knowledge ends up scattered across every single
  document -- as repeated \cs*{definecolor} calls, hard-coded \cs*{fcolorbox}s
  and \cs*{textcolor} calls -- and a later change (a new color scheme, a
  different companion typeface) means hunting down every occurrence
  individually.

  \pkg*{osgstyler} solves this by hiding visual resources (colors, fonts,
  spacing, list appearance, decorations, box appearance) behind a small,
  \emph{portable} vocabulary of names: document code refers to \emph{slots}
  such as \code{primary} or \code{accent}, never to concrete values. A theme
  or palette decides once what sits behind these names; a change there takes
  effect everywhere.

  The package is part of the \pkg*{osglecture} bundle -- \cls*{ltx-talk} uses
  the same color-slot vocabulary as \pkg*{lttheme} --, but can also be used
  completely independently.}

\subsection{\ldeen{Zwei Ebenen: Nutzen und Erstellen}{Two Levels: Using and Creating}}
\label{sec:concepts}
\ldeen{Fast jeder Mechanismus in \osgstyler{} existiert auf zwei Ebenen, und
  diese Dokumentation ist konsequent danach gegliedert:}{Nearly every
  mechanism in \osgstyler{} exists on two levels, and this documentation is
  consistently organized around that distinction:}
\begin{description}
  \item[\ldeen{Nutzen}{Using}] \ldeen{Sie aktivieren eine bereits fertige
    Ressource -- eine mitgelieferte Farb-/Font-/Metrik-/Listenpalette, ein
    bereits vordefiniertes Preset, eine bereits gebundene Umgebung wie
    \env{quote}, einen bereits gebundenen Befehl wie \cs{emph}. Das
    entsprechende Nutzermakro heißt praktisch immer \cs*{UseOsg\ldots}.
    Diese Ebene reicht für die allermeisten Dokumente.}{You activate an
    already-finished resource -- a bundled color/font/metric/list palette, an
    already-defined preset, an already-bound environment such as \env{quote},
    an already-bound command such as \cs{emph}. The corresponding user
    command is almost always named \cs*{UseOsg\ldots}. This level is enough
    for the vast majority of documents.}
  \item[\ldeen{Erstellen}{Creating}] \ldeen{Sie definieren selbst eine neue
    Palette, ein neues Preset, eine neue Dekoration oder eine neue
    Blockinstanz -- etwa für ein eigenes Farbschema oder eine neue Art von
    hervorgehobenem Block. Das entsprechende Makro heißt praktisch immer
    \cs*{DeclareOsg\ldots}. Diese Ebene betrifft typischerweise
    Theme-/Vorlagenautor*innen, nicht jedes einzelne Dokument.}{You define a
    new palette, a new preset, a new decoration, or a new block instance
    yourself -- for example for your own color scheme or a new kind of
    highlighted block. The corresponding command is almost always named
    \cs*{DeclareOsg\ldots}. This level typically concerns theme/template
    authors, not every individual document.}
\end{description}
\ldeen*{Eine dritte, seltener gebrauchte Ebene richtet sich an
  Backend-/Adapter-Autor*innen: Wer entscheidet, \emph{wie} eine Dekoration
  tatsächlich gezeichnet wird (siehe @1) oder wie ein Theme-Paket seine
  Farben automatisch einspeist (siehe @2), tauscht einen sogenannten
  \emph{Prozessor} aus oder registriert einen Hook -- auch das ist möglich,
  aber für gewöhnliche Dokumente nicht nötig.}{A third, more rarely used
  level addresses backend/adapter authors: deciding \emph{how} a decoration
  is actually drawn (see @1) or how a theme package feeds in its colors
  automatically (see @2) means replacing a so-called \emph{processor} or
  registering a hook -- also possible, but not needed for ordinary
  documents.}{\ref{sec:decorations:processors}}{\ref{sec:integration}}

\ldeen*{Innerhalb der Nutzen-/Erstellen-Unterscheidung gibt es außerdem zwei
  \emph{strukturell} verschiedene Arten von Ressourcen, deren Namen sich
  entsprechend unterscheiden:}{Within the using/creating distinction, there
  are also two \emph{structurally} different kinds of resources, and their
  names reflect this:}
\begin{description}
  \item[\ldeen{Paletten}{Palettes}] \ldeen{Farb-, Font-, Metrik- und
    Listenpaletten (\cs{DeclareOsgColorPalette} und Geschwister) sind reine,
    unter einem Namen gruppierte Datenablagen mit einem festen Satz
    \emph{Slots} (z.\,B. \code{primary}, \code{accent}). Sie nutzen kein
    \pkg*{xtemplate}, sondern eine schlanke, eigene Verwaltung dieses
    Pakets -- angemessen, weil eine Palette nur Werte nachschlägt, nicht wie
    eine Umgebung parametrisiert aufgerufen wird.}{Color, font, metric, and
    list palettes (\cs{DeclareOsgColorPalette} and siblings) are pure data
    stores, grouped under a name, with a fixed set of \emph{slots} (e.g.\
    \code{primary}, \code{accent}). They do not use \pkg*{xtemplate}, but a
    lean, package-specific bookkeeping mechanism -- appropriate, since a
    palette only looks up values rather than being invoked with parameters
    like an environment.}
  \item[\ldeen{Templates und Instanzen}{Templates and Instances}]
    \ldeen{Inline-Styles, Blöcke und Nummerierungen (\cs{DeclareOsgInlineStyle},
    \cs{DeclareOsgBlockInstance}, \cs{DeclareOsgNumbering}) sind tatsächliche
    \needpackage[macros/latex/contrib/xtemplate]{xtemplate}-Instanzen: Ein
    einmal deklarierter \emph{Template-Typ} (\code{osgstyler-block} usw.)
    legt eine feste Schnittstelle aus benannten \emph{Schlüsseln} fest (z.\,B.
    \code{rule-thickness}, \code{padding}); eine \emph{Instanz} ist ein
    gespeicherter, benannter Satz von Werten für diese Schlüssel.
    \emph{Traits} (\cs{DeclareOsgBlockTrait}) sind schließlich nur benannte,
    wiederverwendbare Schlüsselfragmente -- eine Abkürzung, um mehrere
    Instanzen dieselbe Kombination von Schlüsseln teilen zu lassen, ohne sie
    zu wiederholen.}{Inline styles, blocks, and numbering
    (\cs{DeclareOsgInlineStyle}, \cs{DeclareOsgBlockInstance},
    \cs{DeclareOsgNumbering}) are actual
    \needpackage[macros/latex/contrib/xtemplate]{xtemplate} instances: a
    \emph{template type} declared once (\code{osgstyler-block} etc.) fixes an
    interface of named \emph{keys} (e.g.\ \code{rule-thickness},
    \code{padding}); an \emph{instance} is a stored, named set of values for
    those keys. \emph{Traits} (\cs{DeclareOsgBlockTrait}), finally, are just
    named, reusable key fragments -- a shortcut for letting several
    instances share the same combination of keys without repeating them.}
\end{description}
\ldeen*{Ein \emph{Preset} wie \code{osl-standard} oder \code{osl-modern}
  bündelt schließlich beides unter einem gemeinsamen Namen: eine Farb-, eine
  Font- und ggf.\ eine Listenpalette sowie ggf.\ einen Block-Trait. @1
  aktiviert alle vorhandenen Teile eines Presets auf einmal -- Details in
  Abschnitt~@2.}{A \emph{preset} such as \code{osl-standard} or
  \code{osl-modern}, finally, bundles both under one shared name: a color, a
  font, and possibly a list palette, plus possibly a block trait. @1
  activates every part of a preset that exists, all at once -- details in
  Section~@2.}{\cs{UseOsgPreset}}{\ref{sec:blocks:presets}}

\begin{infobox}{\ldeen{Namenskonvention}{Naming Convention}}
  \ldeen{Alle öffentlichen Befehle beginnen mit \code{Osg} (oder, bei
    Paketoptionen und Templateschlüsseln, tragen einfach keinen Prefix, da
    sie ohnehin in einem eigenen Namensraum stehen). Befehle, die eine
    \emph{fertige} Ressource auswählen, beginnen mit \cs*{UseOsg\ldots};
    Befehle, die eine \emph{neue} Ressource anlegen, mit
    \cs*{DeclareOsg\ldots}. Diese Konvention gilt ausnahmslos und wird in
    dieser Dokumentation nicht mehr eigens erwähnt.}{All public commands
    begin with \code{Osg} (or, for package options and template keys, simply
    carry no prefix, since they already live in their own namespace).
    Commands that select an \emph{existing} resource begin with
    \cs*{UseOsg\ldots}; commands that create a \emph{new} resource begin with
    \cs*{DeclareOsg\ldots}. This convention holds without exception and is
    not called out again individually in this documentation.}
\end{infobox}

\subsection{\ldeen{Alternativen}{Alternatives}}
\label{sec:alternatives}
\ldeen{Es gibt keine direkte Entsprechung zu \osgstyler{} auf
  \ctan{}\footnote{Jedenfalls ist dem Autor keine bekannt.}, wohl aber
  Pakete, die einzelne Aspekte abdecken.}{There is no direct equivalent to
  \osgstyler{} on \ctan{}\footnote{At least none known to the author.}, but
  there are packages that cover individual aspects.}
\begin{description}
  \item[\pkg{xtemplate}] \ldeen{Der \needpackage[macros/latex/contrib/xtemplate]{xtemplate}-Mechanismus
    selbst (Teil des \LaTeX-Kernels) liefert das Konzept von
    Template-Typen/-Instanzen, das \pkg*{osgstyler} für Blöcke,
    Inline-Styles und Nummerierung nutzt -- aber ohne jedes fertige
    Vokabular für Farben, Schriften oder Boxen. \osgstyler{} lässt sich
    daher auch als eine Sammlung fertiger \pkg*{xtemplate}-Template-Typen
    für Dokumentstile verstehen.}{The \needpackage[macros/latex/contrib/xtemplate]{xtemplate}
    mechanism itself (part of the \LaTeX{} kernel) provides the
    template-type/instance concept that \pkg*{osgstyler} uses for blocks,
    inline styles, and numbering -- but without any ready-made vocabulary
    for colors, fonts, or boxes. \osgstyler{} can therefore also be
    understood as a collection of ready-made \pkg*{xtemplate} template types
    for document styles.}
  \item[\pkg{tcolorbox}/\pkg{mdframed}] \ldeen*{Beide Pakete zeichnen
    Kästen und sind erheblich mächtiger als \osgstyler{}s eigener,
    absichtlich einfacher Block-Renderer (siehe @1). \osgstyler{} löst
    dieses Spannungsfeld, indem der Renderer austauschbar bleibt: eine
    eigene, auf @2 aufbauende Implementierung des \code{osgstyler-block}-Templates
    ist jederzeit möglich, ohne die Instanzen (\env{quote}, eigene
    Theoreme, \ldots) neu deklarieren zu müssen.}{Both packages draw boxes
    and are considerably more powerful than \osgstyler{}'s own,
    deliberately simple block renderer (see @1). \osgstyler{} resolves
    this tension by keeping the renderer replaceable: a custom
    implementation of the \code{osgstyler-block} template built on @2 is
    possible at any time, without having to redeclare the instances
    (\env{quote}, custom theorems, \ldots).}{\ref{sec:blocks:concept}}{\pkg{tcolorbox}}
  \item[\pkg{ulem}/\pkg{soul}/\pkg{lua-ul}] \ldeen{Diese Pakete
    implementieren Unterstreichungen bzw.\ Hervorhebungen auf Zeichenebene.
    \osgstyler{}s Referenzbackend (\pkg{osgstyler-lua}, siehe
    Abschnitt~\ref{sec:lua}) baut direkt auf \needpackage{lua-ul} auf; ein
    Dekorationsbackend auf Basis von \needpackage{soul} oder
    \needpackage{ulem} ist ebenso denkbar.}{These packages implement
    underlining or highlighting at the character level. \osgstyler{}'s
    reference backend (\pkg{osgstyler-lua}, see
    Section~\ref{sec:lua}) is built directly on \needpackage{lua-ul}; a
    decoration backend based on \needpackage{soul} or \needpackage{ulem}
    is equally conceivable.}
  \item[\pkg*{lttheme}] \ldeen{Das Schwesterpaket im \pkg*{osglecture}-Bundle
    stellt Farbthemes für \cls*{ltx-talk} bereit und teilt \osgstyler{}s
    Farb-Slot-Vokabular. Die beiden Pakete sind bewusst voneinander
    entkoppelt (\osgstyler{} hängt nicht von \pkg*{lttheme} ab); wird
    \pkg*{lttheme} aber geladen, synchronisiert \osgstyler{} sich
    automatisch, siehe Abschnitt~\ref{sec:lttheme}.}{The sibling package in
    the \pkg*{osglecture} bundle provides color themes for \cls*{ltx-talk}
    and shares \osgstyler{}'s color-slot vocabulary. The two packages are
    deliberately decoupled (\osgstyler{} does not depend on
    \pkg*{lttheme}); but if \pkg*{lttheme} is loaded, \osgstyler{}
    synchronizes with it automatically, see
    Section~\ref{sec:lttheme}.}
\end{description}

\section{\ldeen{Anwendung}{Using osgstyler}}
\label{sec:using}
\ldeen{Dieser Teil enthält alles, was zum Auswählen und Anwenden fertiger
  Stile nötig ist. Wer nur ein Dokument gestalten möchte, kann ihn der Reihe
  nach lesen und anschließend direkt zur Referenz springen. Die Deklaration
  eigener Paletten, Styles und Umgebungen beginnt erst in
  Abschnitt~\ref{sec:theme-development}.}{This part contains everything
  needed to select and apply ready-made styles. Readers who only want to
  style a document can read it in order and then skip directly to the
  reference. Declaring custom palettes, styles, and environments starts only
  in Section~\ref{sec:theme-development}.}

\subsection{\ldeen{Farbpaletten}{Color Palettes}}
\label{sec:colors}
\ldeen{Eine Farbpalette bildet portable \emph{Slotnamen} auf konkrete
  \pkg*{xcolor}-Farben ab. Genau eine Farbpalette ist zu jedem Zeitpunkt
  \emph{aktiv}; alle anderen Teile von \osgstyler{} (Dekorationen,
  Inline-Styles, Blöcke) verweisen ausschließlich auf Slotnamen, nie auf
  konkrete Farbwerte.}{A color palette maps portable \emph{slot names} to
  concrete \pkg*{xcolor} colors. Exactly one color palette is \emph{active}
  at any time; every other part of \osgstyler{} (decorations, inline styles,
  blocks) refers only to slot names, never to concrete color values.}

\label{sec:colors:use}
\ldeen*{Beim Laden des Pakets ist bereits die eingebaute Palette
  \code{osgstyler-default} aktiv -- \osgstyler{} funktioniert also ohne
  jede Farbkonfiguration. Eine andere Palette wird mit @1
  aktiviert:}{When the package is loaded, the built-in palette
  \code{osgstyler-default} is already active -- \osgstyler{} therefore
  works without any color configuration at all. A different palette is
  activated with @1:}{\cs{UseOsgColorPalette}}
\begin{example}[code-only]
    \UseOsgColorPalette{osl-modern}
\end{example}
\ldeen*{@1/@2 sind zwei fertige, aber nicht automatisch aktive Presets
  (siehe Abschnitt~@3): eine zurückhaltende, dem unkolorierten
  \cls*{amsthm}-Erscheinungsbild nahe Variante für die Langform, und eine an
  einem verbreiteten, modernen Farbsystem orientierte. Beide werden in der
  Praxis eher über @4 als direkt über @5 aktiviert, da @4 zugleich die
  passende Font- und ggf.\ Listenpalette mitnimmt.}{@1/@2 are two ready-made
  but not automatically active presets (see Section~@3): a restrained
  variant close to unadorned \cls*{amsthm} appearance for the long form, and
  one modeled on a widely used, modern color system. In practice, both are
  usually activated via @4 rather than directly via @5, since @4 also picks
  up the matching font and, where available, list palette.}
  {\code{osl-standard}}{\code{osl-modern}}{\ref{sec:blocks:presets}}
  {\cs{UseOsgPreset}}{\cs{UseOsgColorPalette}}

\ldeen*{Eigener Code, der eine Slotfarbe braucht -- etwa in einer eigenen
  Dekoration oder direkt im Dokument --, löst den Slotnamen mit dem
  expandierbaren @1 in einen stabilen \pkg*{xcolor}-Namen auf. Dieser Name
  ändert sich nicht, wenn die aktive Palette wechselt, wohl aber die Farbe,
  auf die er verweist.}{Custom code that needs a slot color -- e.g.\ in a
  custom decoration, or directly in the document -- resolves the slot name
  to a stable \pkg*{xcolor} name with the expandable @1. This name does not
  change when the active palette changes, but the color it refers to
  does.}{\cs{OsgColorName}}
\begin{example}[code-only]
    \textcolor{\OsgColorName{accent}}{Accent-colored text}
\end{example}
\begin{warnbox}{\ldeen{Achtung}{Attention}}
  \ldeen*{Verwenden Sie niemals einen bloßen Slotnamen wie \code{accent}
    direkt als \pkg*{xcolor}-Farbnamen (\cs*{textcolor}\Marg{accent}) --
    dieser Name existiert unter Umständen gar nicht oder verweist auf etwas
    anderes. Nur @1 liefert den unter der aktiven Palette tatsächlich
    definierten Namen.}{Never use a bare slot name such as \code{accent}
    directly as an \pkg*{xcolor} color name (\cs*{textcolor}\Marg{accent})
    -- that name might not exist at all, or might refer to something else.
    Only @1 returns the name actually defined under the active
    palette.}{\cs{OsgColorName}}
\end{warnbox}

\subsection{\ldeen{Schriftpaletten}{Font Palettes}}
\label{sec:fonts}
\ldeen*{Analog zu Farbpaletten bildet eine Schriftpalette portable
  \emph{Slotnamen} auf @1-Ressourcen ab. @1 wird dafür bei Bedarf
  automatisch geladen -- außer die Paketoption @2 ist gesetzt, siehe
  Abschnitt~@3.}{Analogous to color palettes, a font palette maps portable
  \emph{slot names} to @1 resources. @1 is loaded automatically for this
  purpose -- unless the package option @2 is set, see Section~@3.}
  {\needpackage{fontspec}}{\option{colors-only}}{\ref{sec:fonts:colors-only}}

\label{sec:fonts:use}
\ldeen*{Wie bei Farben ist beim Laden bereits eine eingebaute Palette
  (\code{osgstyler-default}, reines Latin Modern) aktiv. Eine andere Palette
  wird mit @1 aktiviert, ein einzelner Slot im Fließtext mit @2
  ausgewählt:}{As with colors, a built-in palette (\code{osgstyler-default},
  plain Latin Modern) is already active when the package is loaded. A
  different palette is activated with @1, and a single slot is selected in
  running text with @2:}{\cs{UseOsgFontPalette}}{\cs{UseOsgFont}}
\begin{example}[code-only]
    \UseOsgFontPalette{osl-modern}

    Fließtext bleibt \code{main}. {\UseOsgFont{companion}Dieser
    Textteil nutzt die Begleitschrift.}
\end{example}
\ldeen*{Der Slot @1 lässt sich nicht auswählen (@2 bricht mit Fehler ab) --
  er ist für einen späteren @3-Adapter reserviert und liefert keine
  eigenständige Textschriftfamilie.}{The slot @1 cannot be selected (@2
  aborts with an error) -- it is reserved for a future @3 adapter and does
  not provide a standalone text font family.}{\code{math}}
  {\cs{UseOsgFont}\Marg{math}}{\needpackage{unicode-math}}

\subsection{\ldeen{Metrikpaletten}{Metric Palettes}}
\label{sec:metrics}
\ldeen*{Eine Metrikpalette bildet portable Größen-Slots auf konkrete
  Längen ab -- für Abstände, Innenabstände (Padding), Linienstärken und
  Eckenradien. Anders als Farb-/Font-Slots sind Metrikwerte in vier
  \emph{Kategorien} gruppiert, jede mit eigenen Slotnamen: @1.}{A metric
  palette maps portable size slots to concrete lengths -- for spacing,
  padding, rule thickness, and corner radii. Unlike color/font slots,
  metric values are grouped into four \emph{categories}, each with its own
  slot names: @1.}{\code{spacing}/\code{padding}/\code{rule}/\code{radius}}

\label{sec:metrics:use}
\ldeen*{Die eingebaute Palette @1 ist beim Laden bereits aktiv. Eine andere
  Palette wird mit @2 aktiviert; der aktive Wert eines Slots wird mit dem
  expandierbaren @3 abgefragt.}{The built-in palette @1 is already active
  when the package is loaded. A different palette is activated with @2; the
  active value of a slot is queried with the expandable @3.}
  {\code{osgstyler-default}}{\cs{UseOsgMetricPalette}}{\cs{OsgMetric}}
\begin{example}[code-only]
    \UseOsgMetricPalette{my-metrics}
    \setlength\fboxsep{\OsgMetric{padding}{normal}}
\end{example}
\ldeen{Eigene Dekorationen und Templates greifen typischerweise nicht
  direkt auf \cs{OsgMetric} zu, sondern erlauben eine Metrikreferenz der
  Form \meta{Kategorie}\code{/}\meta{Slot} an genau der Stelle, an der auch
  eine literale Länge stehen darf (siehe \code{thickness}/\code{padding}
  bei Dekorationen, Abschnitt~\ref{sec:decorations}).}{Custom decorations
  and templates typically don't access \cs{OsgMetric} directly, but instead
  accept a metric reference of the form
  \meta{category}\code{/}\meta{slot} anywhere a literal length is also
  allowed (see \code{thickness}/\code{padding} for decorations,
  Section~\ref{sec:decorations}).}

\subsection{\ldeen{Listenpaletten}{List Palettes}}
\label{sec:lists}
\ldeen*{Anders als die bisherigen Paletten ist eine Listenpalette keine
  reine Datenablage: @1 wendet die hinterlegten Optionen \emph{sofort} über
  @2 an, denn genau das ist auch @2s eigenes Anwendungsmodell -- eine
  Aktivierung gilt ab dem Aufruf, nicht rückwirkend für bereits gesetzte
  Listen. Werte je Ebene sind rohe @2-Optionen; \osgstyler{} erfindet @2s
  Vokabular nicht neu, sondern organisiert es nur unter einem
  Palettennamen.}{Unlike the palettes so far, a list palette is not a pure
  data store: @1 applies the stored options \emph{immediately} via @2,
  since that matches @2's own application model -- an activation takes
  effect from the call onward, not retroactively for lists already
  typeset. Values per level are raw @2 options; \osgstyler{} does not
  reinvent @2's vocabulary, it only organizes it under a palette
  name.}{\cs{UseOsgListPalette}}{\pkg*{enumitem}}

\label{sec:lists:use}
\ldeen*{Die eingebaute Palette @1 lässt @2s eigene Vorgaben unangetastet.
  Eine andere Palette wird mit @3 aktiviert.}{The built-in palette @1
  leaves @2's own defaults untouched. A different palette is activated
  with @3.}{\code{osgstyler-default}}{\pkg*{enumitem}}
  {\cs{UseOsgListPalette}}
\begin{example}[code-only]
    \UseOsgListPalette{osl-modern}
    \begin{itemize}
      \item Farbige quadratische Bullets statt Standard-Bullets.
    \end{itemize}
\end{example}
\ldeen{Nicht deklarierte Ebenen oder Listenarten einer Palette bleiben
  unangetastet -- \code{osl-modern} etwa ändert nur \env{itemize}-Ebene~1,
  Ebenen~2--4 sowie \env{enumerate}/\env{description} bleiben bei
  \pkg*{enumitem}s eigenen Vorgaben.}{Levels or list types not declared in
  a palette stay untouched -- \code{osl-modern}, for instance, only changes
  \env{itemize} level~1; levels~2--4 as well as
  \env{enumerate}/\env{description} keep \pkg*{enumitem}'s own defaults.}

\subsection{\ldeen{Dekorationen}{Decorations}}
\label{sec:decorations}
\ldeen{Eine Dekoration ist ein backend-neutrales visuelles Rezept: eine
  benannte Kombination aus Linien, Hintergrund, Rahmen, Randmarkierung
  und/oder Vor-/Nachzeichen (\emph{Affixe}), beschrieben rein über Werte
  (Farb-Slots, Metrik-Slots, Muster) -- ohne festzulegen, \emph{wie} daraus
  tatsächlich Tinte auf dem Papier wird. Das erledigt ein austauschbarer
  \emph{Prozessor}, siehe Abschnitt~\ref{sec:decorations:processors}.}{A
  decoration is a backend-neutral visual recipe: a named combination of
  lines, background, border, margin marking, and/or leading/trailing marks
  (\emph{affixes}), described purely through values (color slots, metric
  slots, patterns) -- without fixing \emph{how} that actually becomes ink
  on paper. That is handled by a replaceable \emph{processor}, see
  Section~\ref{sec:decorations:processors}.}

\label{sec:decorations:use}
\ldeen*{Die meisten Dokumente berühren Dekorationen nie direkt: die
  eingebauten @1/@2/@3 stecken hinter den gleichnamigen Inline-Styles
  (Abschnitt~@4) und werden über \cs{UseOsgInlineStyle} bzw.\ gebundene
  Befehle wie \cs{uline} genutzt.}{Most documents never touch decorations
  directly: the built-in @1/@2/@3 sit behind the inline styles of the same
  name (Section~@4) and are used via \cs{UseOsgInlineStyle} or bound
  commands such as \cs{uline}.}
  {\code{osgstyler-underline}}{\code{osgstyler-highlight}}
  {\code{osgstyler-frame}}{\ref{sec:inline}}

\begin{warnbox}{\ldeen{Wichtig}{Important}}
  \ldeen*{Eine deklarierte Dekoration zeichnet nichts von selbst. Ob @1
    (Linien, Hintergrund) tatsächlich sichtbar werden, hängt vom aktiven
    \emph{Prozessor} ab: der eingebaute Standardprozessor zeichnet nur @2
    und @3 (siehe Referenztabelle unten); @1 verlangt @4 (siehe
    Abschnitt~@5) oder einen eigenen Prozessor.}{A declared decoration does
    not draw anything by itself. Whether @1 (lines, background) actually
    become visible depends on the active \emph{processor}: the built-in
    default processor only draws @2 and @3 (see the reference table
    below); @1 requires @4 (see Section~@5) or a custom processor.}
    {\code{line}/\code{background}}{\code{enclosure}}{\code{affix}}
    {\pkg{osgstyler-lua}}{\ref{sec:lua}}
\end{warnbox}

\ldeen{Für Theme-/Backend-Code lässt sich abfragen, ob überhaupt ein
  rendernder Prozessor aktiv ist, sowie, welche Komponenten/Instanzen eine
  Dekoration hat, ohne deren konkrete Werte zu kennen:}{Theme/backend code
  can query whether a rendering processor is active at all, as well as
  which components/instances a decoration has, without knowing its concrete
  values:}
\begin{example}[code-only]
    \OsgDecorationProcessorAvailableTF
      { }
      { \PackageWarning{my-theme}{No~decoration~backend~active} }
\end{example}

\subsection{\ldeen{Inline-Styles}{Inline Styles}}
\label{sec:inline}
\ldeen*{Inline-Styles sind die erste Kompositionsebene: eine benannte
  Instanz des Kernel-Template-Typs @1 bündelt Font-/Farbwahl, Schriftschnitt
  und -stärke, eine Dekoration und eine semantische Rolle zu einem einzigen
  wiederverwendbaren Stil.}{Inline styles are the first composition layer:
  a named instance of the kernel template type @1 bundles font/color
  choice, shape and weight, a decoration, and a semantic role into a
  single, reusable style.}{\code{osgstyler-inline}}

\label{sec:inline:use}
\ldeen*{Fünf Styles sind vordefiniert: @1. Direkt aufgerufen wird ein Style
  mit @2:}{Five styles are predefined: @1. A style is invoked directly
  with @2:}
  {\code{emphasis}/\code{strong}/\code{underline}/\code{highlight}/\code{frame}}
  {\cs{UseOsgInlineStyle}}
\begin{example}[code-only]
    \UseOsgInlineStyle{emphasis}{hervorgehobener Text}
\end{example}
\ldeen*{In der Praxis werden Inline-Styles meist nicht direkt aufgerufen,
  sondern über @1 an einen gewohnten Befehlsnamen \emph{gebunden}, sodass
  weiterhin @2, @3 usw.\ geschrieben wird:}{In practice, inline styles are
  usually not invoked directly, but \emph{bound} via @1 to a familiar
  command name, so that @2, @3, etc.\ continue to be written:}
  {\cs{BindOsgInlineCommand}}{\cs{emph}}{\cs{textbf}}
\begin{example}[code-only]
    \UseOsgStandardInlineCommands
    % bindet \emph, \textbf, \uline, \highlight, \fbox
\end{example}
\ldeen*{@1 bindet einzeln nach Bedarf; @2 bindet alle fünf Standardbefehle
  auf einmal. \cs{highlight} ist unabhängig davon bereits beim Laden des
  Pakets gebunden, da es kein vorbestehendes \LaTeX-Kommando gibt, das
  sonst überraschend ersetzt würde.}{@1 binds individually as needed; @2
  binds all five standard commands at once. \cs{highlight} is already
  bound when the package loads, independently of this, since there is no
  pre-existing \LaTeX{} command that would otherwise be surprisingly
  replaced.}{\cs{BindOsgInlineCommand}}{\cs{UseOsgStandardInlineCommands}}

\ldeen*{Eine Bindung ändert die \emph{Implementierung} des Befehls, nie
  seine Aufrufsyntax -- @1 nimmt weiterhin genau ein Argument. War der
  Befehl vor dem Binden bereits definiert (wie @2/@3), bleibt seine
  ursprüngliche Bedeutung als @4-Plug erhalten; @5 (pro Befehl) bzw.\ @6
  (gesammelt) schaltet dorthin zurück. Für neu durch \osgstyler{}
  eingeführte Befehle wie @7 ist der @4-Plug stattdessen die Identität
  (gibt das Argument unverändert zurück).}{A binding changes the command's
  \emph{implementation}, never its call syntax -- @1 continues to take
  exactly one argument. If the command already existed before binding
  (such as @2/@3), its original meaning is preserved as the @4 plug; @5
  (per command) or @6 (collectively) switches back to it. For commands
  newly introduced by \osgstyler{} such as @7, the @4 plug is the identity
  instead (returns the argument unchanged).}
  {\meta{\ldeen{Befehl}{command}}}{\cs{emph}}{\cs{textbf}}
  {\code{default}}{\cs{RestoreOsgInlineCommand}}
  {\cs{UseOsgDefaultInlineCommands}}{\cs{highlight}}

\ldeen{Wie jede \TeX-Zuweisung ist eine Bindung gruppenlokal -- ein Style
  lässt sich also auch nur für einen Abschnitt des Dokuments ändern.}{Like
  any \TeX{} assignment, a binding is group-local -- a style can therefore
  also be changed for only part of the document.}
\begin{example}[code-only]
    {\BindOsgInlineCommand\emph{strong}
     \emph{fett statt kursiv, nur in dieser Gruppe}}
    \emph{wieder wie gewohnt}
\end{example}

\begin{infobox}{\ldeen{Mit osglecture-modes}{With osglecture-modes}}
  \ldeen*{Ist @1 geladen (in beliebiger Reihenfolge zu \osgstyler{}),
    akzeptieren @2 sowie \emph{jeder} über @3 gebundene Befehl zusätzlich
    ein portables Modusargument, siehe dessen Dokumentation.}{If @1 is
    loaded (in either order relative to \osgstyler{}), @2 as well as
    \emph{every} command bound via @3 additionally accepts a portable mode
    expression, see that package's documentation.}
    {\pkg{osglecture-modes}}{\cs{UseOsgInlineStyle}}
    {\cs{BindOsgInlineCommand}}
  \begin{example}[code-only]
      \emph<presentation>{nur im Präsentationsmodus hervorgehoben}
  \end{example}
\end{infobox}

\subsection{\ldeen{Bausteine: Blöcke}{Blocks}}
\label{sec:blocks}
\ldeen{Blöcke sind die zweite Kompositionsebene: kastenartige, umrandete
  oder hinterlegte Inhalte -- Beamer-Blöcke ebenso wie theorem-artige Boxen
  oder Zitate in der Langform. Dies ist der umfangreichste Teil von
  \osgstyler{}, da er sich sowohl an gewöhnliche Dokumente (Zitate, fertige
  Blöcke) als auch an Theme-/Paketautor*innen (eigene Umgebungen,
  \pkg*{amsthm}-Integration) richtet.}{Blocks are the second composition
  layer: boxed, framed, or filled content -- beamer blocks as well as
  theorem-like boxes or quotations in the long form. This is the most
  extensive part of \osgstyler{}, since it addresses both ordinary
  documents (quotations, ready-made blocks) and theme/package authors
  (custom environments, \pkg*{amsthm} integration).}

\subsubsection{\ldeen{Konzept}{Concept}}
\label{sec:blocks:concept}
\ldeen*{Wie Inline-Styles ist ein Block eine Instanz eines
  Kernel-Template-Typs, hier @1. Anders als bei Inline-Styles trennt
  \osgstyler{} bei Blöcken jedoch bewusst zwei unabhängige Fragen:}{Like
  inline styles, a block is an instance of a kernel template type, here
  @1. Unlike inline styles, however, \osgstyler{} deliberately separates
  two independent questions for blocks:}{\code{osgstyler-block}}
\begin{enumerate}
  \item \ldeen{\emph{Welche Optik} hat der Block?}{\emph{What visual
    appearance} does the block have?}
    \ldeen{-- festgelegt durch die Interface-Schlüssel einer Instanz
    (Rahmenfarbe/-stärke, Innenabstand, Hintergrund, Text vor/nach dem
    Inhalt).}{-- determined by an instance's interface keys (rule
    color/thickness, padding, background, text before/after the
    content).}
  \item \ldeen{\emph{Wie kommt Inhalt} in diese Optik hinein?}{\emph{How
    does content get} into that appearance?}
    \ldeen{-- entweder, indem eine bereits bestehende Umgebung (\env{quote},
    ein \pkg*{amsthm}-\env{theorem}, Beamers \env{block}) optisch
    \emph{umhüllt} wird (Abschnitt~\ref{sec:blocks:create}, Fall~A), oder
    indem \osgstyler{} eine \emph{neue} Umgebung von Grund auf erzeugt
    (Fall~B).}{-- either by visually \emph{wrapping} an already-existing
    environment (\env{quote}, an \pkg*{amsthm} \env{theorem}, beamer's
    \env{block}) (Section~\ref{sec:blocks:create}, case~A), or by having
    \osgstyler{} create a \emph{new} environment from scratch (case~B).}
\end{enumerate}
\ldeen*{Diese Trennung bedeutet: die Render-Logik einer Optik existiert
  \emph{einmal}, unabhängig davon, wie viele Umgebungen sie am Ende
  nutzen, und eine umhüllte Umgebung behält ihre eigene Nummerierung,
  ihren eigenen \pkg*{amsthm}-Zähler usw.\ vollständig -- @1 fügt nur
  Optik um deren fertige Ausgabe herum, ohne sie zu interpretieren.}{This
  separation means: the rendering logic for an appearance exists
  \emph{once}, no matter how many environments end up using it, and a
  wrapped environment fully keeps its own numbering, its own
  \pkg*{amsthm} counter, etc.\ -- @1 only adds appearance around its
  finished output, without interpreting it.}
  {\cs{BindOsgBlockEnvironment}}

\begin{warnbox}{\ldeen{Aktueller Stand}{Current Status}}
  \ldeen*{Der Standard-Renderer zeichnet @1: eine farbige Umrandung fester
    Stärke (\pkg*{fbox}-artig), einen Innenabstand und wahlweise einen
    Hintergrund. @2 und @3 wirken nur im Kastenzweig des
    \code{titled}-Layouts (Trait \code{titlebar}) und nur, wenn
    \pkg{pgfcore} geladen ist (etwa durch \pkg*{lttheme}s
    \code{magpie}-Theme); der \code{plain}-Kastenzweig (\code{card},
    \code{framed}, \code{filled}) bleibt vorerst eckig ohne Schatten. Fehlt
    \pkg{pgfcore}, fällt der Renderer auf eckig zurück.}{The default
    renderer draws @1: a colored border of fixed thickness
    (\pkg*{fbox}-style), a padding, and optionally a background. @2 and @3
    take effect only in the boxed branch of the \code{titled} layout (trait
    \code{titlebar}) and only when \pkg{pgfcore} is loaded (e.g.\ by
    \pkg*{lttheme}'s \code{magpie} theme); the \code{plain} boxed branch
    (\code{card}, \code{framed}, \code{filled}) stays square without a
    shadow for now. Without \pkg{pgfcore} the renderer falls back to
    square.}
    {\code{rule-color}/\code{rule-thickness}/\code{padding}/\code{background}}
    {\code{corner-radius}}{\code{shadow}/\code{shadow-offset}/\code{shadow-color}}
    {\ref{sec:blocks:traits}}
\end{warnbox}

\label{sec:blocks:use}
\ldeen*{Fünf Instanzen sind immer schon deklariert (unsichtbar, wie es die
  Interface-Vorgaben ohnehin vorsehen), aber \emph{nicht} automatisch mit
  einer Umgebung verbunden: @1/@2 (ohne eigene Optik) sowie @3/@4/@5 (mit
  Rahmen und Innenabstand; @4/@5 zusätzlich in Alarm-/Beispielfarbe). @6
  aktiviert alle fünf auf einmal:}{Five instances are always already
  declared (invisible, as the interface defaults provide anyway), but
  \emph{not} automatically connected to an environment: @1/@2 (with no
  appearance of their own) as well as @3/@4/@5 (with a border and
  padding; @4/@5 additionally in alert/example color). @6 activates all
  five at once:}
  {\env{quote}}{\env{quotation}}{\env{block}}{\env{alertblock}}
  {\env{exampleblock}}{\cs{UseOsgStandardBlockEnvironments}}
\begin{example}[code-only]
    \UseOsgStandardBlockEnvironments

    \begin{document}
    \begin{quote}
      Ein Zitat, jetzt mit Rahmen aus der aktiven Farbpalette.
    \end{quote}

    \begin{block}{Titel}
      Ein Beamer-artiger Block, auch in der Langform verfügbar.
    \end{block}
    \end{document}
\end{example}
\ldeen*{@1/@2/@3 werden dabei nur dann \emph{neu erzeugt}, wenn sie noch
  nicht existieren (Langform ohne Beamer/\cls*{ltx-talk}): unter
  \cls*{ltx-talk} existieren sie bereits nativ und behalten dort ihre
  eigene Optik -- nur @4/@5 werden dort umgebunden.}{@1/@2/@3 are only
  \emph{newly created} if they do not already exist (long form without
  beamer/\cls*{ltx-talk}): under \cls*{ltx-talk}, they already exist
  natively and keep their own appearance there -- only @4/@5 are rebound
  there.}{\env{block}}{\env{alertblock}}{\env{exampleblock}}
  {\env{quote}}{\env{quotation}}

\begin{warnbox}{\ldeen{Wichtig}{Important}}
  \ldeen*{Nichts davon geschieht beim bloßen Laden von \osgstyler{}. Wer
    das Paket nur wegen Farben oder Inline-Befehlen lädt, soll nicht
    überrascht feststellen, dass sich seine Zitate verändert haben -- @1
    ist ein bewusster, expliziter Opt-in.}{None of this happens merely by
    loading \osgstyler{}. Anyone loading the package only for colors or
    inline commands should not be surprised to find their quotations
    changed -- @1 is a deliberate, explicit opt-in.}
    {\cs{UseOsgStandardBlockEnvironments}}
\end{warnbox}

\subsubsection{\ldeen{Presets}{Presets}}
\label{sec:blocks:presets}
\ldeen*{Ein \emph{Preset} bündelt Farb-, Font- und ggf.\ Listenpalette
  sowie ggf.\ einen Block-Trait unter einem gemeinsamen Namen. @1
  aktiviert alle vorhandenen Teile eines Presets auf einmal.}{A
  \emph{preset} bundles a color, font, and possibly list palette, plus
  possibly a block trait, under one shared name. @1 activates every part
  of a preset that exists, all at once.}{\cs{UseOsgPreset}}
\begin{example}[code-only]
    \UseOsgPreset{osl-modern}
\end{example}
\ldeen{Zwei Presets liegen bei:}{Two presets are bundled:}
\begin{description}
  \item[\code{osl-standard}] \ldeen{Farb-/Font-Palette; zurückhaltend, dem
    unkolorierten \cls*{amsthm}-Erscheinungsbild nahe. Kein Trait, keine
    Listenpalette.}{Color/font palette; restrained, close to unadorned
    \cls*{amsthm} appearance. No trait, no list palette.}
  \item[\code{osl-modern}] \ldeen{Farb-/Font-Palette; Listenpalette mit
    farbigen quadratischen Bullets (Abschnitt~\ref{sec:lists}); Trait
    \code{osl-modern-rules} (siehe unten).}{Color/font palette; list
    palette with colored square bullets (Section~\ref{sec:lists}); trait
    \code{osl-modern-rules} (see below).}
\end{description}
\ldeen*{Der Trait @1 gibt @2/@3 sowie den drei @4-Instanzen (@5/@6/@7)
  eine dünne, akzentfarbene Linie vor und nach dem Inhalt statt eines
  Rahmens (\code{rule-thickness=0pt}) -- passend zum unkastenlosen
  \pkg*{amsthm}-Vorbild, siehe Abschnitt~\ref{sec:blocks:traits}. @8
  wendet ihn automatisch auf alle sieben Instanzen an, sofern sie
  existieren (unabhängig davon, ob deren Umgebungen bereits gebunden
  sind).}{The trait @1 gives @2/@3 as well as the three @4 instances
  (@5/@6/@7) a thin, accent-colored line before and after the content
  instead of a border (\code{rule-thickness=0pt}) -- matching the
  boxless \pkg*{amsthm} model, see Section~\ref{sec:blocks:traits}. @8
  applies it automatically to all seven instances, provided they exist
  (regardless of whether their environments are already bound).}
  {\code{osl-modern-rules}}{\env{quote}}{\env{quotation}}
  {\env{\ldeen{block-artigen}{block-like}}}{\env{block}}{\env{alertblock}}
  {\env{exampleblock}}{\cs{UseOsgPreset}}

\subsection{\ldeen{Nummerierung}{Numbering}}
\label{sec:numbering}
\ldeen*{Eine Nummerierung liefert \emph{ausschließlich} die formatierte
  Zahl (z.\,B. "`2.3"'); weder ein lokalisiertes Label wie "`Satz"' noch
  ein optionaler Autoren-Titel gehören dazu -- beides bleibt Sache der
  Umgebung, die eine Nummerierung nutzt (typischerweise über
  \cs{NewOsgStyledEnvironment}, Abschnitt~\ref{sec:blocks:new}), damit
  \osgstyler{} selbst sprachneutral bleibt.}{A numbering produces
  \emph{only} the formatted number (e.g.\ "`2.3"'); neither a localized
  label such as "`Theorem"' nor an optional author-supplied title belong
  here -- both remain the concern of the environment that uses a
  numbering (typically via \cs{NewOsgStyledEnvironment},
  Section~\ref{sec:blocks:new}), so that \osgstyler{} itself stays
  language-neutral.}

\label{sec:numbering:use}
\ldeen*{Wer eine bereits deklarierte Nummerierung in eigenem Umgebungscode
  braucht (statt sie über \cs{NewOsgStyledEnvironment} automatisch nutzen
  zu lassen): @1 erhöht den zugrundeliegenden Zähler
  (\cs*{refstepcounter}), das expandierbare @2 liefert den formatierten
  Wert.}{Anyone who needs an already-declared numbering in custom
  environment code (rather than letting \cs{NewOsgStyledEnvironment} use
  it automatically): @1 increments the underlying counter
  (\cs*{refstepcounter}), the expandable @2 returns the formatted
  value.}{\cs{StepOsgNumbering}}{\cs{OsgNumberingValue}}
\begin{example}[code-only]
    \StepOsgNumbering{mynote}
    Notiz~\OsgNumberingValue{mynote}
\end{example}

\section{\ldeen{Themen entwickeln}{Developing Themes}}
\label{sec:theme-development}
\ldeen{Dieser Teil richtet sich an Autor*innen von Themen und Vorlagen. Er
  setzt die Begriffe aus dem Anwendungsteil voraus und behandelt das
  Deklarieren neuer Ressourcen sowie die Backend- und Paketintegration.}{This
  part is intended for authors of themes and templates. It assumes the terms
  introduced in the user part and covers declaring new resources as well as
  backend and package integration.}

\subsection{\ldeen{Farbpaletten}{Color Palettes}}
\label{sec:colors:create}
\ldeen*{Eine neue Farbpalette wird mit @1 deklariert. Genau ein Slot,
  \code{primary}, ist Pflicht; alle anderen Slots fallen andernfalls auf
  einen anderen Slot derselben Palette zurück, siehe unten.}{A new color
  palette is declared with @1. Exactly one slot, \code{primary}, is
  required; every other slot otherwise falls back to another slot of the
  same palette, see below.}{\cs{DeclareOsgColorPalette}}
\begin{example}[code-only]
    \DeclareOsgColorPalette{my-theme}{
      primary    = {model=HTML, value=2E4172},
      accent     = {model=HTML, value=E8A33D},
      alert      = {model=RGB,  value={176,0,32}},
      structure  = {alias=primary}
    }
    \UseOsgColorPalette{my-theme}
\end{example}
\ldeen{Jeder Slot ist entweder eine \emph{Ressource} (\code{model}+\code{value},
  in jedem von \pkg*{xcolor} unterstützten Modell, z.\,B. \code{HTML},
  \code{RGB}, \code{gray}) oder ein \emph{Alias} (\code{alias=}\meta{anderer
  Slot} -- wird beim Aktivieren der Palette einmalig aufgelöst und kopiert,
  wie \cs*{colorlet}; ändert sich die Zielfarbe später, muss die Palette
  erneut aktiviert werden, damit der Alias nachzieht). Ein Slot darf nicht
  beides zugleich sein.}{Each slot is either a \emph{resource}
  (\code{model}+\code{value}, in any model \pkg*{xcolor} supports, e.g.\
  \code{HTML}, \code{RGB}, \code{gray}) or an \emph{alias}
  (\code{alias=}\meta{other slot} -- resolved and copied once when the
  palette is activated, like \cs*{colorlet}; if the target color changes
  later, the palette must be reactivated for the alias to follow). A slot
  cannot be both at once.}

\ldeen*{Nicht definierte Slots fallen automatisch zurück, damit eine
  minimale Palette (nur @1) bereits vollständig nutzbar ist:}{Undefined
  slots automatically fall back, so that a minimal palette (only @1) is
  already fully usable:}{\code{primary}}
\begin{center}
\begin{tabular}{ll}
  \textbf{Slot} & \textbf{\ldeen{Rückfall}{Fallback}}\\\hline
  \code{secondary} & \code{primary}\\
  \code{accent} & \code{secondary}\\
  \code{structure}, \code{alert}, \code{example} & \code{primary}\\
  \code{text} & \ldeen{fest}{fixed} \code{black}\\
  \code{background} & \ldeen{fest}{fixed} \code{white}\\\hline
\end{tabular}
\end{center}
\ldeen*{\code{text} und \code{background} fallen bewusst \emph{nicht} auf
  eine andere Themefarbe zurück (alle anderen Slots landen letztlich bei
  @1) -- eine Themefarbe als Fließtext- oder Seitenhintergrundfarbe könnte
  unzureichenden Kontrast verursachen, was für Akzente unproblematisch,
  für Fließtext aber inakzeptabel wäre.}{\code{text} and \code{background}
  deliberately do \emph{not} fall back to another theme color (every other
  slot ultimately lands on @1) -- a theme color as body-text or
  page-background color could cause insufficient contrast, which is
  unproblematic for accents but unacceptable for body text.}{\code{primary}}

\subsection{\ldeen{Schriftpaletten}{Font Palettes}}
\label{sec:fonts:create}
\ldeen*{Eine neue Schriftpalette wird mit @1 deklariert. Der Slot @2 ist
  Pflicht (mit eigener Ressource, kein Alias); alle übrigen Slots sind
  optional.}{A new font palette is declared with @1. The slot @2 is
  required (with its own resource, not an alias); every other slot is
  optional.}{\cs{DeclareOsgFontPalette}}{\code{main}}
\begin{example}[code-only]
    \DeclareOsgFontPalette{my-fonts}{
      main      = {font = {Source Serif 4}},
      companion = {font = {Source Sans 3},
                   features = {Numbers=OldStyle}},
      display   = {alias = companion},
      accent    = {alias = companion},
      mono      = {font = {Source Code Pro}},
      symbol    = {alias = main}
    }
    \UseOsgFontPalette{my-fonts}
\end{example}
\ldeen{Jeder Slot ist entweder eine \emph{Ressource} (\code{font}, optional
  mit \pkg*{fontspec}-\code{features}, sowie optional einer
  \code{description} für Werkzeuge/Themes) oder ein \emph{Alias}
  (\code{alias=}\meta{anderer Slot}). Ein Slot darf nicht beides zugleich
  sein. Anders als bei Farben wird ein Alias bei jeder tatsächlichen
  Auswahl neu aufgelöst, nicht einmalig beim Aktivieren kopiert.}{Each slot
  is either a \emph{resource} (\code{font}, optionally with \pkg*{fontspec}
  \code{features}, plus an optional \code{description} for tools/themes) or
  an \emph{alias} (\code{alias=}\meta{other slot}). A slot cannot be both at
  once. Unlike colors, an alias is re-resolved on every actual selection,
  not copied once when activating.}

\ldeen*{Nur @1 haben eingebaute Rückfälle; @2 selbst ist Pflicht und @3
  hat, anders als alle übrigen Slots, keinen Rückfall -- fehlt @3, bricht
  erst die \emph{Auswahl} von @3 mit Fehler ab, nicht schon die
  Deklaration.}{Only @1 have built-in fallbacks; @2 itself is required, and
  @3, unlike every other slot, has no fallback -- if @3 is missing, only
  \emph{selecting} @3 aborts with an error, not the declaration itself.}
  {\code{companion}/\code{display}/\code{accent}/\code{symbol}}
  {\code{main}}{\code{mono}}
\begin{center}
\begin{tabular}{ll}
  \textbf{Slot} & \textbf{\ldeen{Rückfall}{Fallback}}\\\hline
  \code{companion} & \code{main}\\
  \code{display} & \code{companion}\\
  \code{accent} & \code{companion}\\
  \code{symbol} & \code{main}\\
  \code{mono} & \ldeen{keiner}{none}\\\hline
\end{tabular}
\end{center}

\subsubsection{\ldeen{Engine-neutraler Betrieb}{Engine-Neutral Operation}}
\label{sec:fonts:colors-only}
\ldeen*{@1 verlangt \LuaTeX{} oder \XeTeX{}, was unter @2 (beispielsweise
  @3) kein tragbarer Zwang ist. Die Paketoption @4 hält @5 deshalb
  engine-neutral: @1 wird dann gar nicht erst geladen, und nur die
  \emph{Deklaration} von Schriftpaletten (reine Datenverwaltung) bleibt
  möglich -- @6/@7 quittieren jeden Versuch, tatsächlich einen Font
  auszuwählen oder zu laden, mit einer klaren Fehlermeldung statt eines
  kryptischen Absturzes.}{@1 requires \LuaTeX{} or \XeTeX{}, which is not a
  portable requirement under @2 (e.g.\ @3). The package option @4 therefore
  keeps @5 engine-neutral: @1 is then not loaded at all, and only
  \emph{declaring} font palettes (pure data management) remains possible --
  @6/@7 reject any attempt to actually select or load a font with a clear
  error message instead of a cryptic crash.}
  {\needpackage{fontspec}}{\cls*{ltx-talk}}{pdf\TeX}
  {\option{colors-only}}{osgstyler}{\cs{UseOsgFontPalette}}{\cs{UseOsgFont}}
\begin{example}[code-only]
    \usepackage[colors-only]{osgstyler}
\end{example}
\ldeen*{@1 prüft, ob Font-Auswahl gerade möglich ist -- nützlich für
  Theme-Code, der unter @2 auf reine Farboptik ausweichen will, statt einen
  Fehler zu riskieren.}{@1 checks whether font selection is currently
  possible -- useful for theme code that wants to fall back to color-only
  styling under @2 instead of risking an error.}
  {\cs{OsgFontPaletteAvailableTF}}{\option{colors-only}}

\subsection{\ldeen{Metrikpaletten}{Metric Palettes}}
\label{sec:metrics:create}
\ldeen*{Jede Deklaration startet mit einem \emph{vollständigen} eingebauten
  Wertesatz (siehe Referenztabelle unten); @1 muss deshalb nur die
  tatsächlich abweichenden Werte angeben.}{Every declaration starts from a
  \emph{complete} built-in set of values (see the reference table below);
  @1 therefore only needs to specify the values that actually
  differ.}{\cs{DeclareOsgMetricPalette}}
\begin{example}[code-only]
    \DeclareOsgMetricPalette{my-metrics}{
      spacing = {sm = {.4\baselineskip}, lg = {1.25\baselineskip}},
      rule    = {thin = {.5pt}, thick = {1.5pt}}
    }
    \UseOsgMetricPalette{my-metrics}
\end{example}
\ldeen{Nur die vier genannten Kategorien mit ihren jeweiligen Slots
  existieren; eigene Kategorien oder Slotnamen lassen sich nicht
  hinzufügen.}{Only the four named categories with their respective slots
  exist; custom categories or slot names cannot be added.}

\subsection{\ldeen{Listenpaletten}{List Palettes}}
\label{sec:lists:create}
\ldeen*{Eine neue Listenpalette wird mit @1 deklariert -- pro Listenart
  (@2) ein Satz von Ebenen (\code{1}--\code{4}), deren Wert jeweils rohe
  @3-Optionen für @3s eigenen @4-Aufruf sind.}{A new list palette is
  declared with @1 -- for each list type (@2), a set of levels
  (\code{1}--\code{4}) whose value is raw @3 options for @3's own @4
  call.}{\cs{DeclareOsgListPalette}}
  {\code{itemize}/\allowbreak\code{enumerate}/\allowbreak\code{description}}
  {\pkg*{enumitem}}{\cs*{setlist}}
\begin{example}[code-only]
    \DeclareOsgListPalette{my-lists}{
      itemize = {
        1 = { label = \textcolor{\OsgColorName{accent}}{\textbullet} }
      },
      enumerate = {
        1 = { label = \textbf{\arabic*.} }
      }
    }
    \UseOsgListPalette{my-lists}
\end{example}
\ldeen*{Da \pkg*{enumitem}s eigener Optionsparser sein Argument vor
  Expansion einliest, dürfen Werte hier -- anders als bei Farb-/Font-/
  Metrikpaletten -- Befehle wie \cs{OsgColorName} direkt (unexpandiert)
  enthalten, wie im Beispiel oben.}{Since \pkg*{enumitem}'s own option
  parser reads its argument before expansion, values here -- unlike for
  color/font/metric palettes -- may contain commands such as
  \cs{OsgColorName} directly (unexpanded), as in the example above.}

\subsection{\ldeen{Dekorationen}{Decorations}}
\label{sec:decorations:create}
\ldeen*{Eine Dekoration wird mit @1 deklariert. Jede der fünf Familien
  (@2) kommt entweder in \emph{Kurzform} (eine einzelne, implizit
  \code{default} genannte Instanz) oder mit \emph{benannten} Instanzen --
  nützlich, um z.\,B. gleichzeitig eine Über- und eine Unterlinie
  anzulegen.}{A decoration is declared with @1. Each of the five families
  (@2) comes either in \emph{short form} (a single instance, implicitly
  named \code{default}) or with \emph{named} instances -- useful, for
  example, to set up an overline and an underline at the same time.}
  {\cs{DeclareOsgDecoration}}
  {\code{line}/\allowbreak\code{background}/\allowbreak\code{enclosure}/%
  \allowbreak\code{margin}/\allowbreak\code{affix}}
\begin{example}[code-only]
    \DeclareOsgDecoration{marked-important}{
      line = {
        position=below, pattern=solid, color=alert,
        thickness=.08em, offset=rule/thin
      },
      background = { color=accent, opacity=.25, shape=text },
      affix = {
        warning = { position=before, content={!},
                    color=alert, font=accent, gap=.3em }
      }
    }
\end{example}
\ldeen*{Im Beispiel ist @1 die -- implizite -- \code{default}-Instanz der
  @2-Familie; @3 ist eine eigens benannte Instanz der @4-Familie. Zwei
  gleichzeitige Linien (Über-/Unterlinie) sähen so aus:}{In the example, @1
  is the -- implicit -- \code{default} instance of the @2 family; @3 is a
  specifically named instance of the @4 family. Two simultaneous lines
  (over-/underline) would look like this:}
  {\code{line}}{\code{line}}{\code{warning}}{\code{affix}}
\begin{example}[code-only]
    \DeclareOsgDecoration{over-and-under}{
      line = {
        under = { position=below, pattern=solid, color=accent },
        over  = { position=above, pattern=solid, color=accent }
      }
    }
\end{example}
\ldeen{Werte für \code{thickness}, \code{offset}, \code{padding} und
  \code{gap} akzeptieren entweder eine literale Länge oder eine
  Metrikreferenz \meta{Kategorie}\code{/}\meta{Slot} (Abschnitt
  \ref{sec:metrics}), z.\,B. \code{rule/thin} statt \code{.4pt}. \code{content}
  und \code{symbol} eines \code{affix} schließen sich gegenseitig aus.
  \code{font} und \code{color} verweisen stets auf portable Font-/Farb-Slots,
  nie auf konkrete Ressourcen.}{Values for \code{thickness}, \code{offset},
  \code{padding}, and \code{gap} accept either a literal length or a metric
  reference \meta{category}\code{/}\meta{slot} (Section~\ref{sec:metrics}),
  e.g.\ \code{rule/thin} instead of \code{.4pt}. A \code{content} and a
  \code{symbol} of the same \code{affix} are mutually exclusive.
  \code{font} and \code{color} always refer to portable font/color slots,
  never to concrete resources.}

\subsubsection{\ldeen{Renderstatus je Prozessor}{Rendering Status per Processor}}
\label{sec:decorations:status}
\ldeen*{Nicht jede deklarierbare Eigenschaft wird von den mitgelieferten
  Prozessoren (dem eingebauten Standardprozessor und @1, siehe
  Abschnitt~@2) auch tatsächlich gezeichnet -- manche Eigenschaften sind
  bewusst für einen mächtigeren, künftigen Backend reserviert. Diese
  Tabelle nennt den aktuellen Stand ehrlich:}{Not every declarable property
  is actually drawn by the bundled processors (the built-in default
  processor and @1, see Section~@2) either -- some properties are
  deliberately reserved for a more capable, future backend. This table
  states the current status honestly:}{\pkg{osgstyler-lua}}{\ref{sec:lua}}
\begin{center}
\begin{tabular}{lll}
  \textbf{\ldeen{Familie}{Family}} &
    \textbf{\ldeen{Standardprozessor}{Default processor}} &
    \textbf{\code{osgstyler-lua}}\\\hline
  \code{line} & \ldeen{nicht gezeichnet (Fehler)}{not drawn (error)} &
    \ldeen{\code{solid}/\code{dashed}/\code{dotted}/\code{wavy},
      unten/über/durch}{\code{solid}/\code{dashed}/\code{dotted}/\code{wavy},
      below/above/through}\\
  \code{background} & \ldeen{nicht gezeichnet (Fehler)}{not drawn (error)} &
    \ldeen{mit Opazität und \code{text}/\code{line}-Form}
    {with opacity and \code{text}/\code{line} shape}\\
  \code{enclosure} & \ldeen{eckig, \code{solid}}{square, \code{solid}} &
    \ldeen{Radius; \code{solid}/\code{dashed}/\code{dotted}}
      {radius; \code{solid}/\code{dashed}/\code{dotted}}\\
  \code{margin} & \ldeen{ignoriert}{ignored} &
    \ldeen{nicht gezeichnet (Warnung)}{not drawn (warning)}\\
  \code{affix} & \ldeen{gezeichnet}{drawn} & \ldeen{gezeichnet (dieselbe
    Logik)}{drawn (same logic)}\\\hline
\end{tabular}
\end{center}
\ldeen*{Innerhalb von \code{enclosure} zeichnet der Standardprozessor @1,
  ignoriert aber @2 und @3 (@3 warnt bei Werten ungleich \code{0pt}). Das
  Lua-Backend wertet dagegen Radius sowie solide, gestrichelte und gepunktete
  Rahmen aus.}{Within \code{enclosure}, the default processor draws @1 but
  ignores @2 and @3 (@3 warns for values other than \code{0pt}). The Lua
  backend additionally honours the radius and solid, dashed, and dotted
  strokes.}
  {\code{border-color}/\code{fill-color}/\code{thickness}/\code{padding}}
  {\code{pattern}}{\code{radius}}

\subsubsection{\ldeen{Prozessoren (Backend-Ebene)}{Processors (Backend Level)}}
\label{sec:decorations:processors}
\ldeen*{Eine dritte Ebene neben Nutzen/Erstellen: \emph{wie} eine
  Dekoration gezeichnet wird, legt ein \emph{Prozessor} fest -- eine
  Funktion, die den Dekorationsnamen und den zu dekorierenden Inhalt nimmt
  und dekorierten Inhalt zurückgibt. @1 ersetzt den aktuellen
  Dekorationsprozessor vollständig; @2 tut dasselbe für semantische
  Verarbeitung (siehe Abschnitt~@3). Beide sind ab Werk transparent
  (Dekoration: unterstützt nur @4/@5, siehe oben; Semantik: gibt den
  Inhalt unverändert zurück).}{A third level alongside using/creating:
  \emph{how} a decoration is drawn is determined by a \emph{processor} -- a
  function that takes the decoration name and the content to decorate and
  returns decorated content. @1 replaces the current decoration processor
  entirely; @2 does the same for semantic processing (see
  Section~@3). Both are transparent out of the box (decoration: only
  supports @4/@5, see above; semantics: returns the content
  unchanged).}{\cs{SetOsgDecorationProcessor}}{\cs{SetOsgSemanticProcessor}}
  {\ref{sec:inline}}{\code{enclosure}}{\code{affix}}
\begin{example}[code-only]
    \SetOsgDecorationProcessor{\MyDecorationProcessor}
\end{example}
\ldeen{Dieser Mechanismus richtet sich an Backend-/Themeautor*innen, nicht
  an gewöhnliche Dokumente -- \pkg{osgstyler-lua} (Abschnitt~\ref{sec:lua})
  ist selbst nur ein über \cs{SetOsgDecorationProcessor} installierter
  Prozessor und kann so durch einen eigenen ersetzt werden.}{This mechanism
  targets backend/theme authors, not ordinary documents -- \pkg{osgstyler-lua}
  (Section~\ref{sec:lua}) is itself just a processor installed via
  \cs{SetOsgDecorationProcessor} and can thus be replaced by a custom
  one.}

\subsection{\ldeen{Inline-Styles}{Inline Styles}}
\label{sec:inline:create}
\ldeen*{Ein neuer Stil wird mit @1 deklariert. Alle sechs Schlüssel sind
  optional; fehlt einer, gilt die in der Tabelle genannte Vorgabe.}{A new
  style is declared with @1. All six keys are optional; if one is
  omitted, the default noted in the table applies.}
  {\cs{DeclareOsgInlineStyle}}
\begin{example}[code-only]
    \DeclareOsgInlineStyle{warning-text}{
      color = alert, weight = bold, semantics = none
    }
\end{example}
\begin{center}
\begin{tabular}{lll}
  \textbf{\ldeen{Schlüssel}{Key}} & \textbf{\ldeen{Vorgabe}{Default}} &
    \textbf{\ldeen{Bedeutung von "`keine Änderung"'}{Meaning of ``no
    change''}}\\\hline
  \code{font} & \code{main} & \code{none}\\
  \code{weight} & \code{inherit} & \code{inherit}\\
  \code{shape} & \code{inherit} & \code{inherit}\\
  \code{color} & \code{text} & \code{none}\\
  \code{decoration} & \code{none} & \code{none}\\
  \code{semantics} & \code{none} & \code{none}\\\hline
\end{tabular}
\end{center}
\begin{warnbox}{\ldeen{Achtung}{Attention}}
  \ldeen*{Die Vorgabe für @1 ist @2, nicht @3 -- ein Stil, der @1
    weglässt, wählt bei jeder Verwendung erneut die Haupt\-schrift aus.
    Wer nur Farbe oder Dekoration ändern will, muss @1 explizit auf @3
    setzen (wie es alle fünf eingebauten Stile tun).}{The default for @1
    is @2, not @3 -- a style that omits @1 reselects the main font on
    every use. Anyone who only wants to change color or decoration must
    explicitly set @1 to @3 (as all five built-in styles do).}
    {\code{font}}{\code{main}}{\code{none}}
\end{warnbox}
\ldeen*{\code{weight} kennt @1, \code{shape} kennt @2.}{\code{weight}
  accepts @1, \code{shape} accepts @2.}
  {\code{inherit}/\code{regular}/\code{bold}}
  {\code{inherit}/\code{upright}/\code{italic}/\code{slanted}/\code{small-caps}}
\ldeen*{\code{semantics} verweist auf eine \emph{Rolle} (z.\,B.
  \code{emphasis}, \code{strong}), die -- wie \code{decoration} -- an den
  aktiven Prozessor durchgereicht wird (Abschnitt~@1); ohne installierten
  semantischen Prozessor hat @2 keine sichtbare Wirkung außer der
  Weitergabe.}{\code{semantics} refers to a \emph{role} (e.g.\
  \code{emphasis}, \code{strong}) that -- like \code{decoration} -- is
  passed to the active processor (Section~@1); without an installed
  semantic processor, @2 has no visible effect beyond that hand-off.}
  {\ref{sec:decorations:processors}}{\code{semantics}}

\ldeen*{@1 ändert einen bereits deklarierten Stil (nur die angegebenen
  Schlüssel); für eigene, komplett neue Befehlsbindungen mit mehr als
  einem Argument bietet \osgstyler{} bewusst keinen generischen
  Mechanismus (siehe @2) -- @3 selbst nimmt bei Bedarf per
  \pkg*{xtemplate}\ direkten Zugriff (@4/@5) entgegen.}{@1 changes an
  already-declared style (only the given keys); for custom, entirely new
  command bindings with more than one argument, \osgstyler{} deliberately
  offers no generic mechanism (see @2) -- @3 itself accepts direct
  \pkg*{xtemplate} access (@4/@5) if ever needed.}
  {\cs{EditOsgInlineStyle}}{\ref{sec:blocks:create}}{osgstyler-inline}
  {\cs*{DeclareInstance}}{\cs*{UseInstance}}

\subsection{\ldeen{Bausteine: Blöcke}{Blocks}}
\label{sec:blocks:create}
\ldeen*{Für "`Erstellen"' gibt es bei Blöcken drei unabhängige Bausteine,
  die sich beliebig kombinieren lassen: neue \emph{Instanzen} (Optik),
  \emph{Traits} (wiederverwendbare Optik-Fragmente) und die Verbindung von
  Optik mit Inhalt -- entweder durch \emph{Umhüllen} (Fall~A) einer
  bestehenden Umgebung oder durch \emph{Neuerzeugen} (Fall~B).}{For
  "`creating"', there are three independent building blocks for blocks
  that can be combined freely: new \emph{instances} (appearance),
  \emph{traits} (reusable appearance fragments), and connecting appearance
  with content -- either by \emph{wrapping} (case~A) an existing
  environment or by \emph{creating anew} (case~B).}

\subsubsection{\ldeen{Instanzen}{Instances}}
\label{sec:blocks:instances}
\ldeen*{@1 deklariert eine neue, benannte Optik; @2 ändert eine bereits
  bestehende (nur die angegebenen Schlüssel).}{@1 declares a new, named
  appearance; @2 changes an already-existing one (only the given
  keys).}{\cs{DeclareOsgBlockInstance}}{\cs{StyleOsgBlockInstance}}
\begin{example}[code-only]
    \DeclareOsgBlockInstance{mynote}{
      rule-thickness = .6pt, rule-color = accent, padding = padding/normal
    }
\end{example}

\subsubsection{\ldeen{Traits}{Traits}}
\label{sec:blocks:traits}
\ldeen*{Instanzen sind bei \pkg*{xtemplate} ohnehin nur gespeichertes
  Keyval-Material -- mehrere Schlüssel lassen sich also schon durch
  simples Verketten kombinieren, ohne dass es dafür je eine eigene Instanz
  bräuchte. @1 gibt diesem Verketten nur einen Namen: der Schlüssel @2 (in
  @3/@4) schlägt benannte, mit @1 deklarierte Fragmente nach und reiht sie
  vor die übrigen, unverändert durchgereichten Schlüssel ein.}{Instances
  are just stored keyval material in \pkg*{xtemplate} to begin with --
  several keys can therefore already be combined by simple concatenation,
  without ever needing a dedicated instance for that. @1 merely gives
  this concatenation a name: the key @2 (in @3/@4) looks up named
  fragments declared via @1 and prepends them to the remaining keys,
  which pass through unchanged.}
  {\cs{DeclareOsgBlockTrait}}{\code{traits}}
  {\cs{DeclareOsgBlockInstance}}{\cs{StyleOsgBlockInstance}}
\begin{example}[code-only]
    \DeclareOsgBlockTrait{rounded}{corner-radius = 6pt}
    \DeclareOsgBlockInstance{mynote}{
      traits = {rounded}, rule-thickness = .6pt
    }
\end{example}

\begin{warnbox}{\ldeen{Für Trait-Autor*innen}{For Trait Authors}}
  \ldeen*{Ein @1-Wert durchläuft auf dem Weg von der Deklaration bis zur
    fertigen Instanz mehrere vollständige \pkg*{expl3}-Expansionsrunden.
    Befehle mit eigenem Lookahead auf das nächste Zeichen -- @2s
    optionales Anhebungsargument, @3, @4 -- sowie ein nackter @5
    \emph{innerhalb} eines Schlüsselwerts überstehen das nicht
    zuverlässig und können zu kryptischen Fehlern oder sogar
    Endlosschleifen führen, sobald @6 oder @7 mehr als ein einzelnes,
    einfaches Token enthalten. Enthält ein Trait mehr als reine
    Layout-Schlüssel -- zeichnet er also selbst etwas --, sollte die
    komplette Zeichenlogik stattdessen in einer mit @8 definierten
    ("`protected"') Funktion stecken, die als \emph{ein einziges} Token
    durch alle Runden unangetastet durchgereicht und erst beim
    tatsächlichen Satz ausgeführt wird -- innerhalb dieser Funktion sind
    @2, @3, @4 und @5 wieder ganz normal verwendbar. Der eingebaute Trait
    \code{osl-modern-rules} in \code{osgstyler.dtx} folgt genau diesem
    Muster und ist ein Vorbild für eigene Traits mit
    Zeichenlogik.}{A @1 value passes through
    several full \pkg*{expl3} expansion rounds on its way from
    declaration to the finished instance. Commands with their own
    lookahead for the next character -- @2's optional raise argument, @3,
    @4 -- as well as a bare @5 \emph{inside} a key's value do not reliably
    survive that and can cause cryptic errors or even infinite loops as
    soon as @6 or @7 contain more than a single, simple token. If a trait
    contains more than plain layout keys -- i.e.\ draws something itself
    -- the complete drawing logic should instead live in a ("`protected"')
    function defined with @8, which is passed through all rounds
    unaltered as \emph{a single token} and is only executed at actual
    typesetting time -- inside that function, @2, @3, @4, and @5 are
    perfectly usable again. The built-in trait \code{osl-modern-rules} in
    \code{osgstyler.dtx} follows exactly this pattern and is a template
    for custom traits with drawing logic.}
    {\code{traits}}{\cs*{rule}}{\cs*{vspace}}{\cs*{newline}}{\cs*{par}}
    {\code{before}}{\code{after}}{\cs*{cs\_new\_protected:Npn}}
\end{warnbox}

\subsubsection{\ldeen{Fall A: Bestehende Umgebung umhüllen}{Case A: Wrapping an Existing Environment}}
\label{sec:blocks:bind}
\ldeen*{@1 verallgemeinert das Muster, mit dem \pkg*{lttheme} \env{block}
  umhüllt, für Environments \emph{ohne} Overlay-Syntax: eine einheitliche
  Argumentsignatur (ein optionaler Name vor einem Textkörper), die auch
  auf \pkg*{amsthm}-generierte \env{theorem}-Umgebungen (via
  \cs*{newtheorem}) zutrifft. Das optionale Argument wird unverändert an
  das umhüllte Original weitergereicht, nie selbst interpretiert -- die
  eigene Nummerierung und das Kopf-Rendering von @2 bleiben also
  vollständig erhalten, @1 fügt nur Optik \emph{um} deren fertige Ausgabe
  herum.}{@1 generalizes the pattern \pkg*{lttheme} uses to wrap
  \env{block} for environments \emph{without} overlay syntax: a uniform
  argument signature (an optional name before a text body) that also
  applies to \pkg*{amsthm}-generated \env{theorem} environments (via
  \cs*{newtheorem}). The optional argument is forwarded to the wrapped
  original unchanged, never interpreted here itself -- @2's own numbering
  and heading rendering are therefore fully preserved, @1 only adds
  appearance \emph{around} its finished output.}
  {\cs{BindOsgBlockEnvironment}}{\pkg*{amsthm}}
\begin{example}[code-only]
    \usepackage{amsthm}
    \newtheorem{theorem}{Theorem}

    \DeclareOsgBlockInstance{theorem}{
      rule-thickness = .4pt, padding = .5em
    }
    \BindOsgBlockEnvironment{theorem}{theorem}

    \begin{document}
    \begin{theorem}[Fermat]
      Ein von \osgstyler{} gerahmtes amsthm-Theorem, mit voll
      funktionsfähiger amsthm-Nummerierung und optionalem Namen.
    \end{theorem}
    \end{document}
\end{example}
\ldeen*{Da @1 keinen Kasten braucht (siehe @2), passt hier auch der Trait
  @3 statt eines Rahmens -- exakt das Rezept, das @4 auf seine eigenen
  Block-Instanzen anwendet.}{Since @1 needs no box (see @2), the trait @3
  also fits here instead of a border -- exactly the recipe @4 applies to
  its own block instances.}
  {\pkg*{amsthm}}{\ref{sec:blocks:presets}}{\code{osl-modern-rules}}
  {\cs{UseOsgPreset}}
\begin{example}[code-only]
    \StyleOsgBlockInstance{theorem}{traits = {osl-modern-rules}}
\end{example}

\subsubsection{\ldeen{Fall B: Neue Umgebung erzeugen}{Case B: Creating a New Environment}}
\label{sec:blocks:new}
\ldeen*{@1 deckt den Fall ab, in dem \emph{keine} bereits bestehende
  Umgebung umhüllt wird. @1 selbst bleibt sprachneutral: das Label liefert
  der Aufruf als fertigen Text, \osgstyler{} übersetzt nicht.}{@1 covers
  the case where \emph{no} pre-existing environment is being wrapped. @1
  itself stays language-neutral: the label is supplied by the call as
  ready-made text, \osgstyler{} does not translate.}
  {\cs{NewOsgStyledEnvironment}}
\ldeen*{Ohne optionales @1-Argument (Vorgabe @2) verhält sich das erzeugte
  Environment wie @3: ein optionaler Name in eckigen Klammern, automatisch
  zusammengesetzter Titel aus Label, Nummer und optionalem Namen.}{Without
  the optional @1 argument (default @2), the generated environment
  behaves like @3: an optional name in square brackets, an automatically
  composed title from label, number, and optional name.}
  {\meta{nargs}}{\code{0}}{\cs*{newtheorem}}
\begin{example}[code-only]
    \DeclareOsgNumbering{mynote}{counter = mynote}
    \NewOsgStyledEnvironment{mynote}{mynote}{mynote}{Notiz}

    \begin{document}
    \begin{mynote}[Wichtig]
      Ergibt "`Notiz~1 (Wichtig)"' als Titel.
    \end{mynote}
    \end{document}
\end{example}
\ldeen*{Mit @1 zwischen \code{1} und \code{3} verlangt das Environment
  stattdessen @1 Pflichtargumente; die Titelvorlage wird dann pro
  Verwendung mit den echten Argumenten (@2, \ldots) gefüllt -- eine
  automatische Label+Zahl-Komposition entfällt hier bewusst. Genau das
  reproduziert Beamers eigene @3-Signatur für die Langform:}{With @1
  between \code{1} and \code{3}, the environment instead requires @1
  mandatory arguments; the title template is then filled with the real
  arguments (@2, \ldots) on each use -- automatic label+number
  composition is deliberately absent here. This is exactly what
  reproduces beamer's own @3 signature for the long form:}
  {\meta{nargs}}{\code{\#1}}{\env{block}}
\begin{example}[code-only]
    \NewOsgStyledEnvironment{block}{block}{none}[1]{#1}
    % \begin{block}{Titel} ... \end{block}
\end{example}
\ldeen{Wer Nummerierung mit freiem Titel kombinieren will, bezieht
  \cs{OsgNumberingValue} (Abschnitt~\ref{sec:numbering}) selbst in die
  Titelvorlage ein.}{Anyone who wants to combine numbering with a free
  title includes \cs{OsgNumberingValue} (Section~\ref{sec:numbering}) in
  the title template themselves.}

\ldeen*{Ein zweiter, schlankerer Baustein für den umgekehrten Fall -- ein
  semantisches Zitat trägt keine Titelzeile vor dem Inhalt, sondern
  höchstens eine Quellenangabe danach --: @1.}{A second, leaner building
  block for the reverse case -- a semantic quotation carries no title line
  before its content, only an optional attribution after it --: @1.}
  {\cs{NewOsgAttributedEnvironment}}
\begin{example}[code-only]
    \NewOsgAttributedEnvironment{aquote}{quote}

    \begin{document}
    \begin{aquote}[Antoine de Saint-Exupéry]
      Man sieht nur mit dem Herzen gut.
    \end{aquote}
    \end{document}
\end{example}

\subsection{\ldeen{Nummerierung}{Numbering}}
\label{sec:numbering:create}
\ldeen*{@1 registriert einen neuen \LaTeX-Zähler und legt dessen
  Formatierung fest -- in einem Aufruf, sofort bei der Deklaration (nicht
  erst bei erster Nutzung), da ein zweiter \cs*{newcounter}-Aufruf für
  denselben Namen sonst fehlschlüge.}{@1 registers a new \LaTeX{} counter
  and fixes its formatting -- in one call, immediately upon declaration
  (not only on first use), since a second \cs*{newcounter} call for the
  same name would otherwise fail.}{\cs{DeclareOsgNumbering}}
\begin{example}[code-only]
    \DeclareOsgNumbering{mynote}{
      counter = mynote, format = \arabic{mynote}
    }
\end{example}
\ldeen{Mit \code{within}\meta{=Zähler} wird der neue Zähler bei jedem
  Schritt von \meta{Zähler} zurückgesetzt (wie \cs*{newcounter}s eigenes
  \oarg{within}-Argument) -- z.\,B.\ für kapitelweise Nummerierung.}
  {Using \code{within}\meta{=counter}, the new counter is reset every time
  \meta{counter} steps (like \cs*{newcounter}'s own \oarg{within}
  argument) -- e.\,g.\ for per-chapter numbering.}
\begin{example}[code-only]
    \DeclareOsgNumbering{mynote}{
      counter = mynote, within = chapter,
      format = \thechapter.\arabic{mynote}
    }
\end{example}

\subsection{\ldeen{Integration mit anderen Paketen}{Integration with Other Packages}}
\label{sec:integration}
\ldeen{Diese Integrationen laufen weitgehend automatisch ab -- sie
  betreffen nicht die "`Nutzen/Erstellen"'-Unterscheidung selbst, sondern
  beschreiben, wie \osgstyler{} sich in eine größere Werkzeugkette
  einfügt.}{These integrations run largely automatically -- they do not
  concern the using/creating distinction itself, but describe how
  \osgstyler{} fits into a larger toolchain.}

\subsubsection{osglecture-modes}
\label{sec:integration:modes}
\ldeen*{Ist @1 geladen -- in beliebiger Reihenfolge zu \osgstyler{} --,
  akzeptieren @2 sowie \emph{jeder} über @3 gebundene Befehl zusätzlich
  ein portables Modusargument; über \cs{NewOsgStyledEnvironment} oder @4
  erzeugte Umgebungen erhalten zusätzlich eine führende \cs*{d<>}-Spezifikation.
  Ein inaktiver Modus unterdrückt die Ausgabe vollständig, \emph{einschließlich}
  eines etwaigen Nummerierungsschritts -- ausgeblendeter Inhalt soll keine
  Lücke in der Nummerierung hinterlassen.}{If @1 is loaded -- in either
  order relative to \osgstyler{} --, @2 as well as \emph{every} command
  bound via @3 additionally accepts a portable mode expression;
  environments created via \cs{NewOsgStyledEnvironment} or @4 additionally
  get a leading \cs*{d<>} specification. An inactive mode suppresses
  output entirely, \emph{including} any numbering step -- hidden content
  should not leave a gap in the numbering.}
  {\pkg{osglecture-modes}}
  {\cs{UseOsgInlineStyle}/\allowbreak\cs{UseOsgColorPalette}/\allowbreak%
  \cs{UseOsgFontPalette}/\allowbreak\cs{UseOsgMetricPalette}/\allowbreak%
  \cs{UseOsgListPalette}/\allowbreak\cs{UseOsgPreset}}
  {\cs{BindOsgInlineCommand}}{\cs{BindOsgBlockEnvironment}}
\begin{example}[code-only]
    \emph<presentation>{nur im Präsentationsmodus}
    \UseOsgColorPalette<print>{osl-standard}

    \begin{theorem}<longform|print>[Fermat]
      nur in Langform/Druckmodus
    \end{theorem}
\end{example}
\ldeen{Die Deklarationsbefehle (\cs{DeclareOsgColorPalette} und
  Geschwister) bleiben bewusst modusunabhängig -- eine Palette wird
  einmalig definiert, unabhängig davon, in welchen Moden sie später
  ausgewählt wird.}{The declaration commands (\cs{DeclareOsgColorPalette}
  and siblings) deliberately remain mode-independent -- a palette is
  defined once, regardless of which modes later select it.}

\subsubsection{\pkg*{lttheme}}
\label{sec:lttheme}
\ldeen*{\osgstyler{} hängt bewusst nicht von @1 ab (Abschnitt~@2). Wird
  jedoch @3 geladen, synchronisiert sich \osgstyler{} reaktiv: eine
  Farbpalette namens \code{lttheme} wird aus den gerade aktiven
  @1-Farben erzeugt und aktiviert, sooft @1s eigene
  Theme-/Farbbefehle aufgerufen werden. Dies geschieht vollständig
  automatisch -- ein Dokument, das @3 und \osgstyler{} gemeinsam nutzt,
  muss dafür nichts weiter tun.}{\osgstyler{} deliberately does not
  depend on @1 (Section~@2). However, if @3 is loaded, \osgstyler{}
  synchronizes reactively: a color palette named \code{lttheme} is
  created from the currently active @1 colors and activated whenever @1's
  own theme/color commands are called. This happens fully automatically
  -- a document using @3 together with \osgstyler{} does not need to do
  anything for this.}{\pkg*{lttheme}}{\ref{sec:alternatives}}
  {\pkg{ltxtalk-theme}}
\ldeen*{Für @1-interne Zwecke: ein Paket, das @2 vor dem eigenen @3
  definiert (@1 selbst tut dies), zeigt damit an, dass es seine
  Themefarben bereits selbst über \cs{DeclareOsgColorPalette}/\cs{UseOsgColorPalette}
  einspeist -- die reaktive Integration hier erkennt sich dann als
  überflüssig und tut nichts.}{For @1-internal purposes: a package that
  defines @2 before its own @3 (@1 itself does this) thereby indicates
  that it already feeds its theme colors in itself via
  \cs{DeclareOsgColorPalette}/\cs{UseOsgColorPalette} -- the reactive
  integration here then recognizes itself as redundant and does
  nothing.}{\pkg*{lttheme}}{\cs{LtxtalkUsesOsgstylerNatively}}
  {\cs*{RequirePackage}\Marg{osgstyler}}

\subsubsection{\ldeen{Lua-Referenzbackend}{Lua Reference Backend}}
\label{sec:lua}
\ldeen*{@1 lädt \osgstyler{} zusammen mit @2 und @3, installiert sich
  selbst als Dekorationsprozessor (Abschnitt~\ref{sec:decorations:processors})
  und schließt damit die Renderpipeline für Dekorationen praktisch,
  jenseits der von \osgstyler{} selbst schon gerenderten @4/@5 (siehe
  Abschnitt~\ref{sec:decorations:status}).}{@1 loads \osgstyler{} together
  with @2 and @3, installs itself as the decoration processor
  (Section~\ref{sec:decorations:processors}), and thereby closes the
  rendering pipeline for decorations for practical purposes, beyond the
  @4/@5 that \osgstyler{} itself already renders (see
  Section~\ref{sec:decorations:status}).}
  {\pkg{osgstyler-lua}}{\pkg{luacolor}}{\pkg{lua-ul}}{\code{enclosure}}
  {\code{affix}}
\begin{example}[code-only]
    \usepackage{osgstyler-lua}
\end{example}
\ldeen*{Verlangt @1 (bricht sonst mit einer klaren Fehlermeldung ab).
  Rendert zusätzlich @2 an \code{below}/\code{above}/\code{through} und @3
  über eigene, zeilenbruchfeste @4-Leader. @5 zeichnet abgerundete und
  gemusterte Inline-Rahmen.}{Requires @1 (otherwise aborts with a clear error
  message). Additionally renders @2 at \code{below}/\code{above}/\code{through}
  and @3 through custom line-break-safe @4 leaders. @5 draws rounded and
  patterned inline frames.}{\LuaTeX{}}{\code{line}}{\code{background}}
  {\pkg*{lua-ul}}{\pkg*{pgfcore}}
\ldeen{Das expandierbare \cs{OsgStylerLuaBackendActive} liefert stets den
  Text \code{true} und dient vor allem als Existenztest
  (\cs*{cs\_if\_exist:NTF}), um im Theme-Code gezielt das Lua-Backend --
  nicht nur irgendeinen Prozessor -- zu erkennen.}{The expandable
  \cs{OsgStylerLuaBackendActive} always returns the text \code{true} and
  serves primarily as an existence test (\cs*{cs\_if\_exist:NTF}) for
  theme code to detect the Lua backend specifically -- not just any
  processor.}

\paragraph{\ldeen{Referenz}{Reference}}
\begin{commands}
  \expandable\command{OsgStylerLuaBackendActive}
    \ldeen{Siehe oben.}{See above.}
\end{commands}

\section{\ldeen{Zusammenfassende Referenz}{Summary Reference}}
\label{sec:reference}
\ldeen{Die folgenden, thematisch gegliederten Tabellen fassen die
  öffentliche Schnittstelle zusammen. Ausführliche Erläuterungen und
  Beispiele stehen in den jeweils verlinkten Anwender- und
  Entwicklerabschnitten.}{The following tables summarize the public
  interface, grouped by topic. Detailed explanations and examples are in the
  linked user and developer sections.}

\subsection{\ldeen{Farbpaletten}{Color Palettes}}
\label{sec:colors:ref}
\begin{commands}
  \command{DeclareOsgColorPalette}[\marg{name}\marg{keys}]
    \ldeen{Deklariert eine Farbpalette, siehe Abschnitt~\ref{sec:colors:create}.
    }{Declares a color palette, see Section~\ref{sec:colors:create}.}
  \command{UseOsgColorPalette}[\marg{name}]
    \ldeen{Aktiviert eine deklarierte Palette.}{Activates a declared
    palette.}
  \expandable\command{OsgColorName}[\marg{slot}]
    \ldeen{Expandierbar; liefert den \pkg*{xcolor}-Namen der aktiven Palette
    für \meta{slot} (\code{osgstyler-}\meta{slot}).}{Expandable; returns the
    \pkg*{xcolor} name of the active palette for \meta{slot}
    (\code{osgstyler-}\meta{slot}).}
  \expandable\command{OsgActiveColorPalette}
    \ldeen{Expandierbar; Name der aktiven Farbpalette.}{Expandable; name of
    the active color palette.}
  \expandable\command{OsgColorPaletteValue}[\marg{palette}\marg{slot}\marg{property}]
    \ldeen{Expandierbar; liefert einen rohen, gespeicherten Wert einer --
    auch inaktiven -- Palette, ohne Alias-Auflösung. \meta{property} ist
    \code{kind} (\code{resource} oder \code{alias}), sowie je nach Art
    \code{model}/\code{value} oder \code{alias}.}{Expandable; returns a raw
    stored value of a -- possibly inactive -- palette, without alias
    resolution. \meta{property} is \code{kind} (\code{resource} or
    \code{alias}), plus, depending on the kind, \code{model}/\code{value} or
    \code{alias}.}
  \command{OsgColorPaletteResolvedSlotTo}[\marg{palette}\marg{slot}\marg{tl-var}]
    \ldeen{Löst Alias-Ketten und Rückfälle für \meta{slot} in \meta{palette}
    auf und speichert den resultierenden Ressourcen-Slotnamen (nicht die
    Farbe selbst) in der \pkg*{expl3}-Tokenlistenvariable \meta{tl-var}.}
    {Resolves alias chains and fallbacks for \meta{slot} in \meta{palette}
    and stores the resulting resource slot name (not the color itself) in
    the \pkg*{expl3} token-list variable \meta{tl-var}.}
\end{commands}
\begin{options}
  \keyval{model}{\ldeen{Farbmodell}{color model}}
    \ldeen{Jedes von \pkg*{xcolor} unterstützte Modell, z.\,B. \code{HTML},
    \code{RGB}, \code{cmyk}, \code{gray}.}{Any model supported by
    \pkg*{xcolor}, e.g.\ \code{HTML}, \code{RGB}, \code{cmyk}, \code{gray}.}
  \keyval{value}{\ldeen{Modellwert}{model value}}
    \ldeen{Der zum Modell passende Wert, z.\,B. \code{2E4172} für
    \code{HTML}.}{The value matching the model, e.g.\ \code{2E4172} for
    \code{HTML}.}
  \keyval{alias}{\ldeen{anderer Slot}{other slot}}
    \ldeen{Übernimmt die Farbe eines anderen Slots derselben Palette.}
    {Adopts the color of another slot in the same palette.}
\end{options}
\ldeen{Die portablen Farbslots sind}{The portable color slots are}
\code{primary}, \code{secondary}, \code{accent}, \code{text},
\code{background}, \code{structure}, \code{alert}, \code{example}.

\subsection{\ldeen{Farbserien}{Color Series}}
\label{sec:color-series:ref}
\ldeen{Farbserien sind geordnete Folgen ohne semantische Slotnamen. Sie
  eignen sich etwa für Diagrammreihen oder Annotationskategorien und können
  beliebig viele Einträge enthalten. Jeder Eintrag verwendet dieselbe
  \code{model}/\code{value}-Syntax wie eine Palettenressource.}
  {Color series are ordered sequences without semantic slot names. They suit
  chart data or annotation categories and may contain any number of entries.
  Every entry uses the same \code{model}/\code{value} syntax as a palette
  resource.}
\begin{commands}
  \command{DeclareOsgColorSeries}[\marg{name}\marg{items}]
    \ldeen{Deklariert oder ersetzt eine Farbserie.}{Declares or replaces a
    color series.}
  \command{GenerateOsgColorSeries}[\marg{name}\oarg{keys}]
    \ldeen{Erzeugt unter Lua\LaTeX{} eine qualitative Farbserie aus Farben
    der aktiven Palette.}{Under Lua\LaTeX{}, generates a qualitative color
    series from colors of the active palette.}
  \expandable\command{OsgColorSeriesCount}[\marg{name}]
    \ldeen{Liefert die Anzahl der Einträge.}{Returns the number of entries.}
  \expandable\command{OsgColorSeriesColorName}[\marg{name}\marg{index}]
    \ldeen{Liefert den stabilen \pkg*{xcolor}-Namen des Eintrags mit dem
    einsbasierten \meta{index}.}{Returns the stable \pkg*{xcolor} name of the
    entry at the one-based \meta{index}.}
\end{commands}
\begin{example}[code-only]
  \DeclareOsgColorSeries{categories}{
    {model=HTML, value=1B9E77},
    {model=HTML, value=D95F02},
    {model=HTML, value=7570B3}
  }
  \textcolor{\OsgColorSeriesColorName{categories}{2}}{Second category}
\end{example}
\begin{example}[code-only]
  \GenerateOsgColorSeries{categories}[count=8]
\end{example}
\begin{options}
  \keyval{count}{integer}[8]
    \ldeen{Anzahl der Farben (2--32).}{Number of colors (2--32).}
  \keyval{primary}{color}[\cs{OsgColorName}\{primary\}]
    \ldeen{Erste Ankerfarbe.}{First anchor color.}
  \keyval{accent}{color}[\cs{OsgColorName}\{accent\}]
    \ldeen{Zweite Ankerfarbe, sofern wahrnehmbar verschieden.}
    {Second anchor color, if perceptibly different.}
  \keyval{background}{color}[\cs{OsgColorName}\{background\}]
    \ldeen{Hintergrund für die Kontrastprüfung.}
    {Background used for the contrast check.}
\end{options}
\ldeen{Weitere Kandidaten werden in OKLCH verteilt, in den RGB-Farbraum
  eingepasst, gegen den Hintergrund geprüft und auf möglichst großen
  wahrnehmbaren Abstand ausgewählt. Die Farboptionen akzeptieren beliebige
  \pkg*{xcolor}-Farbnamen.}{Further candidates are distributed in OKLCH,
  mapped into the RGB gamut, checked against the background, and selected for
  large perceptual separation. The color options accept arbitrary
  \pkg*{xcolor} color names.}
\ldeen{Farbserien werden nicht aktiviert und unterstützen keine Aliase;
  ihre Einträge sind ausschließlich durch ihre Position bestimmt.}
  {Color series are not activated and do not support aliases; their entries
  are identified solely by position.}

\subsection{\ldeen{Schriftpaletten}{Font Palettes}}
\label{sec:fonts:ref}
\begin{commands}
  \command{DeclareOsgFontPalette}[\marg{name}\marg{keys}]
    \ldeen{Deklariert eine Schriftpalette, siehe Abschnitt~\ref{sec:fonts:create}.
    }{Declares a font palette, see Section~\ref{sec:fonts:create}.}
  \command{UseOsgFontPalette}[\marg{name}]
    \ldeen{Aktiviert eine deklarierte Palette.}{Activates a declared
    palette.}
  \command{UseOsgFont}[\marg{slot}]
    \ldeen{Wählt die Schrift von \meta{slot} in der aktiven Palette aus
    (\cs*{fontfamily}\ldots\cs*{selectfont}); \meta{slot} darf nicht
    \code{math} sein.}{Selects the font of \meta{slot} in the active
    palette (\cs*{fontfamily}\ldots\cs*{selectfont}); \meta{slot} must not
    be \code{math}.}
  \expandable\command{OsgActiveFontPalette}
    \ldeen{Expandierbar; Name der aktiven Schriftpalette.}{Expandable; name
    of the active font palette.}
  \expandable\command{OsgFontPaletteValue}[\marg{palette}\marg{slot}\marg{property}]
    \ldeen{Expandierbar; roher, gespeicherter Wert (\code{kind},
    \code{font}/\code{features} oder \code{alias}, \code{description}) --
    ohne Alias-Auflösung.}{Expandable; raw stored value (\code{kind},
    \code{font}/\code{features} or \code{alias}, \code{description}) --
    without alias resolution.}
  \command{OsgFontPaletteResolvedSlotTo}[\marg{palette}\marg{slot}\marg{tl-var}]
    \ldeen{Löst Alias-Ketten und Rückfälle auf, speichert den
    Ressourcen-Slotnamen in \meta{tl-var}.}{Resolves alias chains and
    fallbacks, stores the resource slot name in \meta{tl-var}.}
  \command{OsgFontPaletteAvailableTF}[\marg{true}\marg{false}]
    \ldeen{Siehe Abschnitt~\ref{sec:fonts:colors-only}.}{See
    Section~\ref{sec:fonts:colors-only}.}
\end{commands}
\begin{options}
  \keyval{font}{\ldeen{Fontname}{font name}}
    \ldeen{Ein \pkg*{fontspec}-Fontname.}{A \pkg*{fontspec} font name.}
  \keyval{features}{\pkg*{fontspec} \ldeen{Optionen}{options}}
    \ldeen{Werden unverändert an \pkg*{fontspec} weitergereicht.}{Passed
    through to \pkg*{fontspec} unchanged.}
  \keyval{alias}{\ldeen{anderer Slot}{other slot}}
    \ldeen{Übernimmt die Schrift eines anderen Slots derselben Palette.}
    {Adopts the font of another slot in the same palette.}
  \keyval{description}{\ldeen{Text}{text}}
    \ldeen{Rein informativ, für Werkzeuge/Themes; wird von \osgstyler{}
    selbst nicht ausgewertet.}{Purely informational, for tools/themes; not
    evaluated by \osgstyler{} itself.}
\end{options}
\ldeen{Die portablen Fontslots sind}{The portable font slots are}
\code{main}, \code{companion}, \code{display}, \code{accent}, \code{mono},
\code{math}, \code{symbol}.
\begin{options}
  \opt{colors-only}
    \ldeen{Paketoption (kein Templateschlüssel); siehe
    Abschnitt~\ref{sec:fonts:colors-only}.}{Package option (not a template
    key); see Section~\ref{sec:fonts:colors-only}.}
\end{options}

\subsection{\ldeen{Metrikpaletten}{Metric Palettes}}
\label{sec:metrics:ref}
\begin{commands}
  \command{DeclareOsgMetricPalette}[\marg{name}\marg{keys}]
    \ldeen{Deklariert eine Metrikpalette, siehe
    Abschnitt~\ref{sec:metrics:create}.}{Declares a metric palette, see
    Section~\ref{sec:metrics:create}.}
  \command{UseOsgMetricPalette}[\marg{name}]
    \ldeen{Aktiviert eine deklarierte Palette.}{Activates a declared
    palette.}
  \expandable\command{OsgMetric}[\marg{category}\marg{slot}]
    \ldeen{Expandierbar; aktueller Wert von \meta{slot} in
    \meta{category} der aktiven Palette.}{Expandable; current value of
    \meta{slot} in \meta{category} of the active palette.}
  \expandable\command{OsgMetricPaletteValue}[\marg{palette}\marg{category}\marg{slot}]
    \ldeen{Expandierbar; Wert einer -- auch inaktiven -- Palette.}
    {Expandable; value of a -- possibly inactive -- palette.}
  \expandable\command{OsgActiveMetricPalette}
    \ldeen{Expandierbar; Name der aktiven Metrikpalette.}{Expandable; name
    of the active metric palette.}
\end{commands}
\begin{center}
\begin{tabular}{lll}
  \textbf{\ldeen{Kategorie}{Category}} & \textbf{\ldeen{Slots}{Slots}} &
    \textbf{\ldeen{Vorgabe}{Default}}\\\hline
  \code{spacing} & \code{none}\ldots\code{xl} &
    $0$, $.25$, $.5$, $1$, $1.5$, $2\times$\code{\textbackslash baselineskip}\\
  \code{padding} & \code{none}\ldots\code{roomy} &
    $0$, $.35$\code{em}, $.65$\code{em}, $1$\code{em}\\
  \code{rule} & \code{hairline}\ldots\code{thick} &
    $.2$\code{pt}, $.4$\code{pt}, $.7$\code{pt}, $1.2$\code{pt}\\
  \code{radius} & \code{square}\ldots\code{round} &
    $0$, $2$\code{pt}, $4$\code{pt}, $6$\code{pt}\\\hline
\end{tabular}
\end{center}
\ldeen*{Die Slots je Kategorie sind im Detail: @1 (\code{spacing}); @2
  (\code{padding}); @3 (\code{rule}); @4 (\code{radius}).}{The slots per
  category, in detail: @1 (\code{spacing}); @2 (\code{padding}); @3
  (\code{rule}); @4 (\code{radius}).}
  {\code{none}/\code{xs}/\code{sm}/\code{md}/\code{lg}/\code{xl}}
  {\code{none}/\code{compact}/\code{normal}/\code{roomy}}
  {\code{hairline}/\code{thin}/\code{normal}/\code{thick}}
  {\code{square}/\code{small}/\code{medium}/\code{round}}

\subsection{\ldeen{Listenpaletten}{List Palettes}}
\label{sec:lists:ref}
\begin{commands}
  \command{DeclareOsgListPalette}[\marg{name}\marg{keys}]
    \ldeen{Deklariert eine Listenpalette, siehe
    Abschnitt~\ref{sec:lists:create}.}{Declares a list palette, see
    Section~\ref{sec:lists:create}.}
  \command{UseOsgListPalette}[\marg{name}]
    \ldeen{Aktiviert eine deklarierte Palette \emph{sofort} (siehe
    oben).}{Activates a declared palette \emph{immediately} (see
    above).}
\end{commands}
\begin{options}
  \keyval{itemize}{\marg{\meta{level}=\marg{\ldeen{enumitem-Optionen}
    {enumitem options}},\ldots}}
    \ldeen{Optionen je Ebene für \env{itemize}.}{Options per level for
    \env{itemize}.}
  \keyval{enumerate}{\marg{\meta{level}=\marg{\ldeen{enumitem-Optionen}
    {enumitem options}},\ldots}}
    \ldeen{Optionen je Ebene für \env{enumerate}.}{Options per level for
    \env{enumerate}.}
  \keyval{description}{\marg{\meta{level}=\marg{\ldeen{enumitem-Optionen}
    {enumitem options}},\ldots}}
    \ldeen{Optionen je Ebene für \env{description}.}{Options per level for
    \env{description}.}
\end{options}
\ldeen{Gültige Ebenen sind \code{1} bis \code{4}.}{Valid levels are
  \code{1} through \code{4}.}

\subsection{\ldeen{Dekorationen}{Decorations}}
\label{sec:decorations:ref}
\begin{commands}
  \command{DeclareOsgDecoration}[\marg{name}\marg{keys}]
    \ldeen{Deklariert eine Dekoration, siehe
    Abschnitt~\ref{sec:decorations:create}.}{Declares a decoration, see
    Section~\ref{sec:decorations:create}.}
  \expandable\command{OsgDecorationValue}[\marg{name}\marg{family}\marg{property}]
    \ldeen{Expandierbar; Eigenschaft der \code{default}-Instanz.}
    {Expandable; property of the \code{default} instance.}
  \expandable\command{OsgDecorationInstanceValue}[\marg{name}\marg{family}\marg{instance}\marg{property}]
    \ldeen{Expandierbar; Eigenschaft einer benannten Instanz.}{Expandable;
    property of a named instance.}
  \expandable\command{OsgDecorationInstances}[\marg{name}\marg{family}]
    \ldeen{Expandierbar; Kommaliste aller Instanznamen einer Familie.}
    {Expandable; comma list of all instance names of a family.}
  \expandable\command{OsgDecorationFamilies}
    \ldeen{Expandierbar; Kommaliste aller fünf Familiennamen.}{Expandable;
    comma list of all five family names.}
  \command{OsgDecorationIfHasComponentTF}[\marg{name}\marg{family}\marg{true}\marg{false}]
    \ldeen{Ob \meta{name} die Familie \meta{family} überhaupt nutzt.}
    {Whether \meta{name} uses family \meta{family} at all.}
  \command{OsgDecorationIfHasInstanceTF}[\marg{name}\marg{family}\marg{instance}\marg{true}\marg{false}]
    \ldeen{Ob eine bestimmte Instanz existiert.}{Whether a specific
    instance exists.}
  \command{OsgDecorationProcessorAvailableTF}[\marg{true}\marg{false}]
    \ldeen{Ob ein rendernder (nicht der transparente Standard-)Prozessor
    aktiv ist.}{Whether a rendering (not the transparent default)
    processor is active.}
  \command{SetOsgDecorationProcessor}[\marg{\ldeen{Funktion}{function}}]
    \ldeen{Siehe Abschnitt~\ref{sec:decorations:processors}.}{See
    Section~\ref{sec:decorations:processors}.}
  \command{SetOsgSemanticProcessor}[\marg{\ldeen{Funktion}{function}}]
    \ldeen{Siehe Abschnitt~\ref{sec:decorations:processors} und
    \ref{sec:inline}.}{See Section~\ref{sec:decorations:processors} and
    \ref{sec:inline}.}
\end{commands}
\begin{options}
  \keyval{position}{\ldeen{siehe Text}{see text}}
    \ldeen{\code{line}/\code{margin}: \code{above}/\code{below} (\code{line}
    zusätzlich \code{through}); \code{affix}: \code{before}/\code{after}.}
    {\code{line}/\code{margin}: \code{above}/\code{below} (\code{line}
    additionally \code{through}); \code{affix}: \code{before}/\code{after}.}
  \keyval{pattern}{\code{solid}, \code{dashed}, \code{dotted}, \code{wavy}}
    \ldeen{Das Lua-Backend unterstützt alle vier Werte für Linien und die
    ersten drei für Rahmen; siehe Abschnitt~\ref{sec:decorations:status}.}
    {The Lua backend supports all four values for lines and the first three
    for enclosures; see Section~\ref{sec:decorations:status}.}
  \keyval{color}{\ldeen{Farb-Slot}{color slot}}
    \ldeen{Siehe Abschnitt~\ref{sec:colors}.}{See Section~\ref{sec:colors}.}
  \keyval{thickness}{\ldeen{Länge oder Metrikreferenz}{length or metric
    reference}} \ldeen{Siehe Abschnitt~\ref{sec:metrics}.}{See
    Section~\ref{sec:metrics}.}
  \keyval{offset}{\ldeen{Länge oder Metrikreferenz}{length or metric
    reference}}
    \ldeen{Siehe Abschnitt~\ref{sec:metrics}.}{See Section~\ref{sec:metrics}.}
  \keyval{opacity}{\meta{\ldeen{Zahl}{number}}}
    \ldeen{Das Lua-Backend rendert Werte von \code{0} bis \code{1}.}
      {The Lua backend renders values from \code{0} to \code{1}.}
  \keyval{shape}{\code{text}, \code{line}}
    \ldeen{Glyphen- bzw. zeilenhohes Hintergrundband im Lua-Backend.}
      {Glyph-height or line-height background band in the Lua backend.}
  \keyval{border-color}{\ldeen{Farb-Slot}{color slot}}
    \ldeen{Siehe Abschnitt~\ref{sec:colors}.}{See Section~\ref{sec:colors}.}
  \keyval{fill-color}{\ldeen{Farb-Slot}{color slot}}
    \ldeen{Siehe Abschnitt~\ref{sec:colors}.}{See Section~\ref{sec:colors}.}
  \keyval{radius}{\ldeen{Länge oder Metrikreferenz}{length or metric reference}}
    \ldeen{Vom Lua-Backend gerendert; der Standardprozessor bleibt eckig.}
      {Rendered by the Lua backend; the default processor remains square.}
  \keyval{padding}{\ldeen{Länge oder Metrikreferenz}{length or metric
    reference}}
    \ldeen{Siehe Abschnitt~\ref{sec:metrics}.}{See Section~\ref{sec:metrics}.}
  \keyval{side}{\ldeen{siehe \pkg*{enumitem}-Dokumentation}
    {see \pkg*{enumitem} documentation}}
    \ldeen{Von keinem mitgelieferten Prozessor ausgewertet, siehe
    Abschnitt~\ref{sec:decorations:status}.}{Not evaluated by any bundled
    processor, see Section~\ref{sec:decorations:status}.}
  \keyval{content}{\ldeen{literaler Inhalt}{literal content}}
    \ldeen{Schließt \code{symbol} aus.}{Mutually exclusive with
    \code{symbol}.}
  \keyval{symbol}{\ldeen{Symbolreferenz}{symbol reference}}
    \ldeen{Schließt \code{content} aus; aktuell löst kein mitgeliefertes
    Backend Symbolreferenzen auf.}{Mutually exclusive with \code{content};
    no bundled backend currently resolves symbol references.}
  \keyval{font}{\ldeen{Font-Slot}{font slot}}
    \ldeen{Siehe Abschnitt~\ref{sec:fonts}.}{See Section~\ref{sec:fonts}.}
  \keyval{gap}{\ldeen{Länge oder Metrikreferenz}{length or metric
    reference}}
    \ldeen{Siehe Abschnitt~\ref{sec:metrics}.}{See Section~\ref{sec:metrics}.}
\end{options}

\subsection{\ldeen{Inline-Styles}{Inline Styles}}
\label{sec:inline:ref}
\begin{commands}
  \command{DeclareOsgInlineStyle}[\marg{name}\marg{keys}]
    \ldeen{Deklariert einen Inline-Style, siehe
    Abschnitt~\ref{sec:inline:create}.}{Declares an inline style, see
    Section~\ref{sec:inline:create}.}
  \command{EditOsgInlineStyle}[\marg{name}\marg{keys}]
    \ldeen{Ändert einen bereits deklarierten Style.}{Changes an
    already-declared style.}
  \command{UseOsgInlineStyle}[\marg{name}\marg{\ldeen{Text}{text}}]
    \ldeen{Setzt \meta{Text} im Stil \meta{name}.}{Typesets \meta{text} in
    style \meta{name}.}
  \command{BindOsgInlineCommand}[\marg{\ldeen{Befehl}{command}}\marg{name}]
    \ldeen{Bindet \meta{Befehl} (ein Argument) an den Stil \meta{name},
    siehe Abschnitt~\ref{sec:inline:use}.}{Binds \meta{command} (one
    argument) to the style \meta{name}, see Section~\ref{sec:inline:use}.}
  \command{RestoreOsgInlineCommand}[\marg{\ldeen{Befehl}{command}}]
    \ldeen{Schaltet \meta{Befehl} auf seinen \code{default}-Plug zurück.}
    {Switches \meta{command} back to its \code{default} plug.}
  \command{UseOsgStandardInlineCommands}
    \ldeen{Bindet \cs{emph}, \cs{textbf}, \cs{uline}, \cs{highlight},
    \cs{fbox} an die gleichnamigen Styles.}{Binds \cs{emph}, \cs{textbf},
    \cs{uline}, \cs{highlight}, \cs{fbox} to the styles of the same name.}
  \command{UseOsgDefaultInlineCommands}
    \ldeen{Schaltet alle bisher gebundenen Befehle gesammelt auf ihren
    \code{default}-Plug zurück.}{Switches all commands bound so far back
    to their \code{default} plug, collectively.}
\end{commands}
\begin{options}
  \keyval{font}{\ldeen{Font-Slot}{font slot}\code{|none}}\Default{main}
    \ldeen{Siehe Abschnitt~\ref{sec:fonts}.}{See Section~\ref{sec:fonts}.}
  \keychoice{weight}{inherit,regular,bold}\Default{inherit}
  \keychoice{shape}{inherit,upright,italic,slanted,small-caps}\Default{inherit}
  \keyval{color}{\ldeen{Farb-Slot}{color slot}\code{|none}}\Default{text}
    \ldeen{Siehe Abschnitt~\ref{sec:colors}.}{See Section~\ref{sec:colors}.}
  \keyval{decoration}{\ldeen{Dekorationsname}{decoration name}\code{|none}}
    \Default{none}
    \ldeen{Siehe Abschnitt~\ref{sec:decorations}.}{See
    Section~\ref{sec:decorations}.}
  \keyval{semantics}{\ldeen{Rolle}{role}\code{|none}}\Default{none}
\end{options}
\ldeen{Vordefinierte Styles: \code{emphasis}, \code{strong},
  \code{underline}, \code{highlight}, \code{frame}. Vordefinierte
  Dekorationen: \code{osgstyler-underline}, \code{osgstyler-highlight},
  \code{osgstyler-frame} (Abschnitt~\ref{sec:decorations}).}{Predefined
  styles: \code{emphasis}, \code{strong}, \code{underline},
  \code{highlight}, \code{frame}. Predefined decorations:
  \code{osgstyler-underline}, \code{osgstyler-highlight},
  \code{osgstyler-frame} (Section~\ref{sec:decorations}).}

\subsection{\ldeen{Bausteine: Blöcke}{Blocks}}
\label{sec:blocks:ref}
\begin{commands}
  \command{DeclareOsgBlockInstance}[\marg{name}\marg{keys}]
    \ldeen{Siehe Abschnitt~\ref{sec:blocks:instances}.}{See
    Section~\ref{sec:blocks:instances}.}
  \command{StyleOsgBlockInstance}[\marg{name}\marg{keys}]
    \ldeen{Siehe Abschnitt~\ref{sec:blocks:instances}.}{See
    Section~\ref{sec:blocks:instances}.}
  \command{DeclareOsgBlockTrait}[\marg{name}\marg{keys}]
    \ldeen{Siehe Abschnitt~\ref{sec:blocks:traits}.}{See
    Section~\ref{sec:blocks:traits}.}
  \command{BindOsgBlockEnvironment}[\marg{\ldeen{Umgebung}{environment}}\marg{name}]
    \ldeen{Siehe Abschnitt~\ref{sec:blocks:bind}.}{See
    Section~\ref{sec:blocks:bind}.}
  \command{NewOsgStyledEnvironment}[\marg{name}\marg{\ldeen{Instanz}{instance}}%
    \marg{\ldeen{Nummerierung}{numbering}\code{|none}}\oarg{nargs}\marg{\ldeen{Titelvorlage}{title template}}]
    \ldeen{\meta{nargs} ist \code{0} bis \code{3}, Vorgabe \code{0}. Siehe
    Abschnitt~\ref{sec:blocks:new}.}{\meta{nargs} is \code{0} to \code{3},
    default \code{0}. See Section~\ref{sec:blocks:new}.}
  \command{NewOsgAttributedEnvironment}[\marg{name}\marg{\ldeen{Instanz}{instance}}]
    \ldeen{Siehe Abschnitt~\ref{sec:blocks:new}.}{See
    Section~\ref{sec:blocks:new}.}
  \command{UseOsgStandardBlockEnvironments}
    \ldeen{Siehe Abschnitt~\ref{sec:blocks:use}.}{See
    Section~\ref{sec:blocks:use}.}
  \command{UseOsgPreset}[\marg{name}]
    \ldeen{Siehe Abschnitt~\ref{sec:blocks:presets}.}{See
    Section~\ref{sec:blocks:presets}.}
\end{commands}
\begin{options}
  \keyval{background}{\ldeen{Farb-Slot}{color slot}\code{|none}}\Default{none}
    \ldeen{Siehe Abschnitt~\ref{sec:colors}. \code{none} bedeutet
    \code{background} (der Farbslot).}{See Section~\ref{sec:colors}.
    \code{none} means \code{background} (the color slot).}
  \keyval{rule-color}{\ldeen{Farb-Slot}{color slot}}\Default{structure}
  \keyval{rule-thickness}{\ldeen{Länge}{length}}\Default{0pt}
    \ldeen{\code{0pt} zeichnet keinen Rahmen.}{\code{0pt} draws no
    border.}
  \keyval{padding}{\ldeen{Länge}{length}}\Default{0pt}
  \keyval{padding-top, padding-right, padding-bottom, padding-left}
    {\ldeen{Länge}{length}}
    \ldeen{Überschreiben den entsprechenden Anteil von \code{padding}.}
      {Override the corresponding side of \code{padding}.}
  \keyval{corner-radius}{\ldeen{Länge}{length}}\Default{0pt}
    \ldeen{Nur im \code{titlebar}-Kastenzweig und nur mit \pkg{pgfcore},
    siehe Abschnitt~\ref{sec:blocks:concept}.}{Only in the \code{titlebar}
    boxed branch and only with \pkg{pgfcore}, see
    Section~\ref{sec:blocks:concept}.}
  \keybool{shadow}\Default{false}
    \ldeen{Wie \code{corner-radius}: nur \code{titlebar} mit
    \pkg{pgfcore}.}{Like \code{corner-radius}: \code{titlebar} with
    \pkg{pgfcore} only.}
  \keyval{shadow-offset}{\ldeen{Länge}{length}}\Default{2pt}
    \ldeen{Versatz des Schattens nach rechts unten.}{Shadow offset towards
    the lower right.}
  \keyval{shadow-color}{\ldeen{Farb-Slot}{color slot}}\Default{structure!30}
    \ldeen{Farbe der Schattenfläche.}{Colour of the shadow area.}
  \keyval{before}{\ldeen{Inhalt}{content}}\Default{}
    \ldeen{Wird vor dem Inhalt gesetzt; siehe die Warnung zu Traits
    (Abschnitt~\ref{sec:blocks:traits}) bei komplexerem Inhalt.}{Typeset
    before the content; see the warning on traits
    (Section~\ref{sec:blocks:traits}) for more complex content.}
  \keyval{after}{\ldeen{Inhalt}{content}}\Default{}
    \ldeen{Wie \code{before}, nach dem Inhalt.}{Like \code{before}, after
    the content.}
  \keyval{traits}{\marg{name,\ldots}}
    \ldeen{Nur bei \cs{DeclareOsgBlockInstance}/\cs{StyleOsgBlockInstance},
    kein Interface-Schlüssel selbst. Siehe
    Abschnitt~\ref{sec:blocks:traits}.}{Only for
    \cs{DeclareOsgBlockInstance}/\cs{StyleOsgBlockInstance}, not an
    interface key itself. See Section~\ref{sec:blocks:traits}.}
  \keychoice{layout}{plain,titled}\Default{plain}
    \ldeen{\code{plain}: Titel (sofern die aufrufende Umgebung einen
    liefert) dem Body vorangestellt, optionaler Rahmen um alles.
    \code{titled}: farbiger Kopfstreifen mit dem Titel, Body darunter --
    fließend, solange \code{body-background} \code{none} ist, sonst als
    Kasten mit Unterkante.}
    {\code{plain}: title (if the calling environment supplies one)
    prepended to the body, optional frame around all. \code{titled}:
    a coloured head strip carrying the title, body below -- flowing while
    \code{body-background} is \code{none}, otherwise a box with a bottom
    edge.}
  \keyval{head-background, head-color, head-rule-color}{\ldeen{Farb-Slot}{color slot}}
    \Default{structure, background, structure}
    \ldeen{Nur bei \code{layout=titled}: Fläche, Schrift und Unterlinie des
    Kopfstreifens.}{Only with \code{layout=titled}: fill, text and
    underline of the head strip.}
  \keyval{head-font}{\ldeen{Inhalt}{content}}\Default{\cs{normalfont}\cs{bfseries}}
  \keyval{head-padding, head-rule-thickness, body-padding}{\ldeen{Länge}{length}}
    \Default{0.35em, 0pt, 0.4em}
  \keyval{head-padding-top, head-padding-right, head-padding-bottom,
      head-padding-left, body-padding-top, body-padding-right,
      body-padding-bottom, body-padding-left}{\ldeen{Länge}{length}}
    \ldeen{Überschreiben jeweils die entsprechende Seite des zugehörigen
      Sammelschlüssels.}{Override the corresponding side of the associated
      shorthand key.}
  \keyval{body-background}{\ldeen{Farb-Slot}{color slot}\code{|none}}\Default{none}
    \ldeen{Nur bei \code{layout=titled}; ein anderer Wert als \code{none}
    schaltet den Kastenzweig ein (\code{rule-thickness}/\code{rule-color}
    gelten dann für dessen Rand).}{Only with \code{layout=titled}; any
    value other than \code{none} switches on the boxed branch
    (\code{rule-thickness}/\code{rule-color} then apply to its border).}
\end{options}
\ldeen{Immer deklarierte Instanzen: \env{quote}, \env{quotation},
  \env{block}, \env{alertblock}, \env{exampleblock}. Presets:
  \code{osl-standard}, \code{osl-modern}. Traits: \code{osl-modern-rules},
  \code{plain}, \code{framed}, \code{filled}, \code{titlebar}, \code{soft},
  \code{heading}, \code{card}. Der Test
  \cs{OsgIfBlockTitledTF}\marg{Name}\marg{ja}\marg{nein} meldet, ob eine
  Instanz \code{layout=titled} verwendet -- umhüllende Pakete brauchen das,
  weil ein Kopf-Titel anders zugeführt werden muss.}
  {Always-declared instances: \env{quote}, \env{quotation}, \env{block},
  \env{alertblock}, \env{exampleblock}. Presets: \code{osl-standard},
  \code{osl-modern}. Traits: \code{osl-modern-rules}, \code{plain},
  \code{framed}, \code{filled}, \code{titlebar}, \code{soft},
  \code{heading}, \code{card}. The test
  \cs{OsgIfBlockTitledTF}\marg{name}\marg{yes}\marg{no} reports whether an
  instance uses \code{layout=titled} -- wrapping packages need this because
  a head title has to be fed in differently.}

\subsection{\ldeen{Nummerierung}{Numbering}}
\label{sec:numbering:ref}
\begin{commands}
  \command{DeclareOsgNumbering}[\marg{name}\marg{keys}]
    \ldeen{Siehe Abschnitt~\ref{sec:numbering:create}.}{See
    Section~\ref{sec:numbering:create}.}
  \command{StepOsgNumbering}[\marg{name}]
    \ldeen{Siehe Abschnitt~\ref{sec:numbering:use}.}{See
    Section~\ref{sec:numbering:use}.}
  \expandable\command{OsgNumberingValue}[\marg{name}]
    \ldeen{Siehe Abschnitt~\ref{sec:numbering:use}.}{See
    Section~\ref{sec:numbering:use}.}
  \expandable\command{OsgNumberingCounter}[\marg{name}]
    \ldeen{Expandierbar; Name des zugrundeliegenden \LaTeX-Zählers (für
    Code, der z.\,B.\ \cs*{value} direkt braucht).}{Expandable; name of
    the underlying \LaTeX{} counter (for code that needs, e.g.,
    \cs*{value} directly).}
\end{commands}
\begin{options}
  \keyval{counter}{\ldeen{Zählername}{counter name}}
    \ldeen{Pflicht; muss ein noch nicht existierender Zählername sein.}
    {Required; must be a not-yet-existing counter name.}
  \keyval{within}{\ldeen{übergeordneter Zähler}{parent counter}\code{|none}}
    \Default{none}
  \keyval{format}{\ldeen{formatierter Wert}{formatted value}}
    \ldeen{Typischerweise \cs*{arabic}\marg{Zähler} oder ähnlich; jeder
    Inhalt, den \cs{OsgNumberingValue} unverändert zurückgeben soll.}
    {Typically \cs*{arabic}\marg{counter} or similar; any content that
    \cs{OsgNumberingValue} should return unchanged.}
\end{options}

\section{FAQ}
\label{sec:faq}
\begin{itemize}
  \item \textbf{\ldeen{Ist \osgstyler{} stabil?}{Is \osgstyler{} stable?}}

    \ldeen*{Nein, noch nicht: \cs{OsgStylerAPIVersion} steht bei
      Drucklegung dieser Dokumentation bei @1, die öffentliche
      Schnittstelle also vor der ersten stabilen Version. Namen und
      Details können sich noch ändern. Bitte melden Sie gefundene Fehler
      über @2.}{No, not yet: \cs{OsgStylerAPIVersion} stands at @1 as
      this documentation goes to press, i.e.\ the public interface is
      still pre-1.0. Names and details may still change. Please report
      any bugs you find via @2.}{\UseVersionOf{osgstyler.sty}}
      {\url{https://github.com/werner-matthias/osglecture/issues}}

  \item \textbf{\ldeen{Wofür steht das "`Osg"' im Namensprefix?}
      {What does the "`Osg"' in the name prefix stand for?}}

    \ldeen{Das Paket gehört zum Bundle \pkg{osglecture}, von dort ist das
      "`Osg"' geerbt.}{The package belongs to the \pkg{osglecture} bundle,
      from which the "`Osg"' is inherited.}

  \item \textbf{\ldeen{Brauche ich \pkg*{lttheme} oder
      \pkg{osglecture-modes}?}{Do I need \pkg*{lttheme} or
      \pkg{osglecture-modes}?}}

    \ldeen{Nein. \osgstyler{} ist vollständig unabhängig nutzbar; beide
      Integrationen (Abschnitt~\ref{sec:integration}) laufen nur an, wenn
      das jeweils andere Paket ohnehin geladen wird.}{No. \osgstyler{} can
      be used completely independently; both integrations
      (Section~\ref{sec:integration}) only engage if the respective other
      package is loaded anyway.}

  \item \textbf{\ldeen{Meine Dekoration (Linie/Hintergrund) erscheint
      nicht.}{My decoration (line/background) does not appear.}}

    \ldeen*{Der eingebaute Standardprozessor rendert @1 nicht (nur @2/@3),
      siehe Abschnitt~\ref{sec:decorations:status}. Laden Sie @4 oder
      installieren Sie einen eigenen Prozessor.}{The built-in default
      processor does not render @1 (only @2/@3), see
      Section~\ref{sec:decorations:status}. Load @4 or install a custom
      processor.}{\code{line}/\code{background}}{\code{enclosure}}
      {\code{affix}}{\pkg{osgstyler-lua}}

  \item \textbf{\ldeen{Mein eigener Trait bzw.\ meine eigene Instanz führt
      zu kryptischen Fehlern oder hängt sich beim Kompilieren auf.}{My
      custom trait or instance causes cryptic errors or hangs during
      compilation.}}

    \ldeen*{Vermutlich enthält ein @1/@2-Wert einen Befehl mit eigenem
      Lookahead (@3, @4, @5) oder einen nackten @6 -- siehe die Warnung in
      Abschnitt~\ref{sec:blocks:traits} und wickeln Sie die Zeichenlogik
      in eine mit @7 definierte Funktion.}{Likely a @1/@2 value contains a
      command with its own lookahead (@3, @4, @5) or a bare @6 -- see the
      warning in Section~\ref{sec:blocks:traits} and wrap the drawing
      logic in a function defined with @7.}
      {\code{before}}{\code{after}}{\cs*{rule}}{\cs*{vspace}}
      {\cs*{newline}}{\cs*{par}}{\cs*{cs\_new\_protected:Npn}}

  \item \textbf{\ldeen{Bricht ein gerahmtes Zitat/Block über eine
      Seitengrenze um?}{Does a framed quotation/block break across a page
      boundary?}}

    \ldeen*{Nein: Der Standard-Renderer setzt den Inhalt bei @1 in eine
      @2 innerhalb einer @3 -- beides unteilbar. Bei sehr langen
      gerahmten Blöcken kann das zu ungünstigem Umbruch oder einem
      Überlaufen der Seite führen. Ein rahmenloser Trait wie
      \code{osl-modern-rules} (Abschnitt~\ref{sec:blocks:traits}) ist
      davon nicht betroffen, da dann kein @2 gezeichnet wird.}{No: the
      default renderer places the content at @1 inside a @2 within a @3 --
      both unbreakable. For very long framed blocks, this can lead to
      awkward breaking or content overflowing the page. A borderless
      trait such as \code{osl-modern-rules} (Section~\ref{sec:blocks:traits})
      is unaffected, since no @2 is drawn in that case.}
      {\code{rule-thickness}\code{>0pt}}{\cs*{fcolorbox}}
      {\cs*{minipage}}

  \item \textbf{\ldeen{Kann ich Beamers eigenes \env{block} an
      \code{osgstyler-block}-Optik binden?}{Can I bind beamer's own
      \env{block} to \code{osgstyler-block} appearance?}}

    \ldeen*{Nicht mit @1: Beamers native @2/@3/@4 haben eine eigene,
      overlay-fähige Signatur, die nicht dem einfachen \oarg{Name}+Textkörper-Muster
      entspricht, das @1 voraussetzt. Unter Beamer/\cls*{ltx-talk} lässt
      \cs{UseOsgStandardBlockEnvironments} @2/@3/@4 deshalb bewusst
      unangetastet (Abschnitt~\ref{sec:blocks:use}) -- nur in der
      Langform werden sie neu erzeugt.}{Not with @1: beamer's native
      @2/@3/@4 have their own overlay-capable signature that does not
      match the simple \oarg{name}+body pattern @1 assumes. Under
      beamer/\cls*{ltx-talk}, \cs{UseOsgStandardBlockEnvironments}
      therefore deliberately leaves @2/@3/@4 untouched
      (Section~\ref{sec:blocks:use}) -- they are only newly created in
      the long form.}{\cs{BindOsgBlockEnvironment}}{\env{block}}
      {\env{alertblock}}{\env{exampleblock}}

  \item \textbf{\ldeen{Meine Frage erscheint hier nicht.}{My question does
      not appear here.}}

    \ldeen*{Sie können sie unter @1 stellen.}{You may ask at @1.}
      {\url{https://github.com/werner-matthias/osglecture/issues}}
\end{itemize}

\printindex

\end{document}
%</driver>
%<*pkg>
% Engine-neutral, unlike fontspec/the Lua decoration backend, so it is
% required unconditionally rather than gated behind 'colors-only'.
\RequirePackage{xcolor}
\RequirePackage{enumitem}

\ExplSyntaxOn
% \ldeen*{@1 hält osgstyler engine-neutral, wenn nur Farb-/Metrik-/
% Dekorationsverwaltung gebraucht wird: @2 verlangt \LuaTeX{} oder
% \XeTeX{} (über @3), was unter @4 kein tragbarer Zwang ist. Deklarieren
% von Font-Paletten bleibt auch unter @1 erlaubt (reine Datenverwaltung,
% siehe @5); nur das tatsächliche Laden/Auswählen eines Fonts über @6/@7
% verlangt @2 und wird unter @1 stattdessen mit einer klaren
% Fehlermeldung quittiert. @8 lässt Themepakete diesen Unterschied
% zur Übersetzungszeit abfragen.}{@1 keeps osgstyler engine-neutral when
% only color/metric/decoration management is needed: @2 requires
% \LuaTeX{} or \XeTeX{} (via @3), which is not a portable requirement
% under @4. Declaring font palettes remains available under @1 (pure
% data management, see @5); only actually loading/selecting a font via
% @6/@7 requires @2 and instead raises a clear error under @1. @8 lets
% theme packages query this distinction at typesetting time.}
% {\option{colors-only}}{\pkg*{fontspec}}{\pkg*{iftex}}
% {\cls*{ltx-talk}}{\cs{DeclareOsgFontPalette}}{\cs{UseOsgFontPalette}}
% {\cs{UseOsgFont}}{\cs{OsgFontPaletteAvailableTF}}
\bool_new:N \g__osgstyler_colors_only_bool
\DeclareKeys
  {
    colors-only .bool_gset:N = \g__osgstyler_colors_only_bool,
    colors-only .initial:n = false,
    colors-only .usage:n = load,
  }
\ProcessKeyOptions\relax
\bool_if:NF \g__osgstyler_colors_only_bool { \RequirePackage{fontspec} }

\NewExpandableDocumentCommand \OsgStylerAPIVersion { } { 0.2.0 }

\cs_generate_variant:Nn \clist_map_inline:nn { V }
\cs_generate_variant:Nn \color_set:nnn { VVV }

% Font palettes contain a deliberately small, portable set of relational
% slots.  A slot either owns a fontspec resource or aliases another slot.
% The distinction is explicit so that a font called, for example, "main"
% can never be mistaken for an alias.
\clist_const:Nn \c__osgstyler_font_slots_clist
  { main, companion, display, accent, mono, math, symbol }

\prop_new:N \g__osgstyler_font_palette_prop
\prop_new:N \g__osgstyler_font_family_prop
\tl_new:N \g__osgstyler_font_palette_tl
\tl_new:N \l__osgstyler_font_name_tl
\tl_new:N \l__osgstyler_font_features_tl
\tl_new:N \l__osgstyler_font_alias_tl
\tl_new:N \l__osgstyler_font_description_tl
\tl_new:N \l__osgstyler_font_resolution_kind_tl
\tl_new:N \l__osgstyler_font_resolution_target_tl
\tl_new:N \l__osgstyler_font_resolution_discard_tl
\tl_new:N \l__osgstyler_font_load_name_tl
\tl_new:N \l__osgstyler_font_load_features_tl
\tl_new:N \l__osgstyler_font_load_family_tl
\seq_new:N \l__osgstyler_font_resolution_seq

\msg_new:nnnn { osgstyler } { unknown-font-slot }
  { Unknown~font~palette~slot~'#1'. }
  { The~portable~slots~are~main,~companion,~display,~accent,~mono,~math,~and~symbol. }
\msg_new:nnnn { osgstyler } { incomplete-font-resource }
  { Font~slot~'#2'~in~palette~'#1'~does~not~specify~a~font~or~an~alias. }
  { Use~font={...}~for~a~resource~or~alias=...~for~an~alias. }
\msg_new:nnnn { osgstyler } { conflicting-font-resource }
  { Font~slot~'#2'~in~palette~'#1'~specifies~both~a~font~and~an~alias. }
  { A~slot~must~either~own~a~fontspec~resource~or~alias~another~slot. }
\msg_new:nnnn { osgstyler } { missing-main-font }
  { Font~palette~'#1'~does~not~define~the~required~slot~'main'. }
  { Add~main={font={...}}~to~the~palette. }
\msg_new:nnnn { osgstyler } { unknown-font-palette }
  { Unknown~font~palette~'#1'. }
  { Declare~it~with~\token_to_str:N\DeclareOsgFontPalette~before~using~it. }
\msg_new:nnnn { osgstyler } { unavailable-font-slot }
  { Font~slot~'#2'~is~not~available~in~palette~'#1'. }
  { Define~that~slot~or~one~of~its~standard~fallbacks. }
\msg_new:nnnn { osgstyler } { font-alias-cycle }
  { Font~palette~'#1'~contains~an~alias~cycle~at~slot~'#2'. }
  { Change~the~aliases~so~that~they~eventually~resolve~to~a~font~resource. }
\msg_new:nnnn { osgstyler } { math-font-selection }
  { Font~slot~'math'~cannot~be~selected~as~a~text~font. }
  { Query~it~for~a~unicode-math~adapter;~use~another~slot~for~text. }
\msg_new:nnnn { osgstyler } { font-management-unavailable }
  { Font~management~is~not~available~(osgstyler~was~loaded~with~the~
    'colors-only'~option). }
  { Check~\token_to_str:N\OsgFontPaletteAvailableTF~before~calling~font~
    commands,~or~load~osgstyler~without~'colors-only'. }

\keys_define:nn { osgstyler / font-resource }
  {
    font .tl_set:N = \l__osgstyler_font_name_tl,
    features .tl_set:N = \l__osgstyler_font_features_tl,
    alias .tl_set:N = \l__osgstyler_font_alias_tl,
    description .tl_set:N = \l__osgstyler_font_description_tl,
  }

\cs_new_protected:Npn \__osgstyler_font_resource_store:nnn #1#2#3
  {
    \tl_clear:N \l__osgstyler_font_name_tl
    \tl_clear:N \l__osgstyler_font_features_tl
    \tl_clear:N \l__osgstyler_font_alias_tl
    \tl_clear:N \l__osgstyler_font_description_tl
    \keys_set:nn { osgstyler / font-resource } {#3}
    \bool_lazy_and:nnTF
      { ! \tl_if_empty_p:N \l__osgstyler_font_name_tl }
      { ! \tl_if_empty_p:N \l__osgstyler_font_alias_tl }
      { \msg_error:nnnn { osgstyler } { conflicting-font-resource } {#1} {#2} }
      {
        \tl_if_empty:NTF \l__osgstyler_font_name_tl
          {
            \tl_if_empty:NTF \l__osgstyler_font_alias_tl
              { \msg_error:nnnn { osgstyler } { incomplete-font-resource } {#1} {#2} }
              {
                \clist_if_in:NVTF \c__osgstyler_font_slots_clist
                  \l__osgstyler_font_alias_tl
                  {
                    \prop_gput:Nne \g__osgstyler_font_palette_prop
                      { #1 / #2 / kind } { alias }
                    \prop_gput:NnV \g__osgstyler_font_palette_prop
                      { #1 / #2 / alias } \l__osgstyler_font_alias_tl
                  }
                  { \msg_error:nne { osgstyler } { unknown-font-slot }
                      \l__osgstyler_font_alias_tl }
              }
          }
          {
            \prop_gput:Nnn \g__osgstyler_font_palette_prop
              { #1 / #2 / kind } { resource }
            \prop_gput:NnV \g__osgstyler_font_palette_prop
              { #1 / #2 / font } \l__osgstyler_font_name_tl
            \prop_gput:NnV \g__osgstyler_font_palette_prop
              { #1 / #2 / features } \l__osgstyler_font_features_tl
          }
        \tl_if_empty:NF \l__osgstyler_font_description_tl
          { \prop_gput:NnV \g__osgstyler_font_palette_prop
              { #1 / #2 / description } \l__osgstyler_font_description_tl }
      }
  }

\cs_new_protected:Npn \__osgstyler_font_palette_declare:nn #1#2
  {
    \clist_map_inline:Nn \c__osgstyler_font_slots_clist
      {
        \clist_map_inline:nn { kind, font, features, alias, description }
          { \prop_gremove:Nn \g__osgstyler_font_palette_prop
              { #1 / ##1 / ####1 } }
      }
    \prop_gremove:Nn \g__osgstyler_font_palette_prop { #1 / declared }
    \clist_map_inline:Nn \c__osgstyler_font_slots_clist
      {
        \keys_define:nn { osgstyler / font-palette / #1 }
          { ##1 .code:n = \__osgstyler_font_resource_store:nnn {#1}{##1}{####1} }
      }
    \keys_set:nn { osgstyler / font-palette / #1 } {#2}
    \prop_if_in:NnF \g__osgstyler_font_palette_prop { #1 / main / kind }
      { \msg_error:nnn { osgstyler } { missing-main-font } {#1} }
    \prop_gput:Nnn \g__osgstyler_font_palette_prop { #1 / declared } { true }
  }

\NewDocumentCommand \DeclareOsgFontPalette { m m }
  { \__osgstyler_font_palette_declare:nn {#1}{#2} }

% \ldeen{Die Standardrückfallebenen der Font gehen vom der konzeptionellen Eignung aus, 
% nicht vom Aussehen}{Default fallbacks deliberately express suitability, not morphology}.
\cs_new:Npn \__osgstyler_font_fallback:n #1
  {
    \str_case:nnF {#1}
      {
        { companion } { main }
        { display }   { companion }
        { accent }    { companion }
        { symbol }    { main }
      }
      { }
  }

\cs_new_protected:Npn \__osgstyler_font_resolve:nnN #1#2#3
  {
    \seq_if_in:NnTF \l__osgstyler_font_resolution_seq {#2}
      {
        \msg_error:nnnn { osgstyler } { font-alias-cycle } {#1}{#2}
        \tl_clear:N #3
      }
      {
        \seq_put_right:Nn \l__osgstyler_font_resolution_seq {#2}
        \prop_get:NnNTF \g__osgstyler_font_palette_prop
          { #1 / #2 / kind } \l__osgstyler_font_resolution_kind_tl
          {
            \str_if_eq:VnTF \l__osgstyler_font_resolution_kind_tl { resource }
              { \tl_set:Nn #3 {#2} }
              {
                \prop_get:NnN \g__osgstyler_font_palette_prop
                  { #1 / #2 / alias } \l__osgstyler_font_resolution_target_tl
                \exp_args:NnV \__osgstyler_font_resolve:nnN {#1}
                  \l__osgstyler_font_resolution_target_tl #3
              }
          }
          {
            \tl_set:Ne \l__osgstyler_font_resolution_target_tl
              { \__osgstyler_font_fallback:n {#2} }
            \tl_if_empty:NTF \l__osgstyler_font_resolution_target_tl
              {
                \msg_error:nnnn { osgstyler } { unavailable-font-slot } {#1}{#2}
                \tl_clear:N #3
              }
              { \exp_args:NnV \__osgstyler_font_resolve:nnN {#1}
                  \l__osgstyler_font_resolution_target_tl #3 }
          }
        \seq_pop_right:NN \l__osgstyler_font_resolution_seq
          \l__osgstyler_font_resolution_discard_tl
      }
  }

\cs_new_protected:Npn \__osgstyler_font_slot_resolve:nnN #1#2#3
  {
    \clist_if_in:NnTF \c__osgstyler_font_slots_clist {#2}
      {
        \seq_clear:N \l__osgstyler_font_resolution_seq
        \__osgstyler_font_resolve:nnN {#1}{#2} #3
      }
      {
        \msg_error:nnn { osgstyler } { unknown-font-slot } {#2}
        \tl_clear:N #3
      }
  }

\NewDocumentCommand \OsgFontPaletteResolvedSlotTo { m m m }
  { \__osgstyler_font_slot_resolve:nnN {#1}{#2} #3 }

\NewExpandableDocumentCommand \OsgFontPaletteValue { m m m }
  { \prop_item:Nn \g__osgstyler_font_palette_prop { #1 / #2 / #3 } }

\NewDocumentCommand \OsgFontPaletteAvailableTF { m m }
  { \bool_if:NTF \g__osgstyler_colors_only_bool {#2}{#1} }

\cs_new_protected:Npn \__osgstyler_font_palette_use:n #1
  {
    \bool_if:NTF \g__osgstyler_colors_only_bool
      { \msg_error:nn { osgstyler } { font-management-unavailable } }
      {
        \prop_if_in:NnTF \g__osgstyler_font_palette_prop { #1 / declared }
          {
            \tl_gset:Nn \g__osgstyler_font_palette_tl {#1}
            \clist_map_inline:Nn \c__osgstyler_font_slots_clist
              {
                \str_if_eq:nnF {##1}{math}
                  {
                    \__osgstyler_font_slot_resolve:nnN {#1}{##1} \l_tmpa_tl
                    \tl_if_empty:NF \l_tmpa_tl
                      {
                        \prop_if_in:NeF \g__osgstyler_font_family_prop
                          { #1 / \l_tmpa_tl }
                          {
                            \prop_get:NeN \g__osgstyler_font_palette_prop
                              { #1 / \l_tmpa_tl / font }
                              \l__osgstyler_font_load_name_tl
                            \prop_get:NeN \g__osgstyler_font_palette_prop
                              { #1 / \l_tmpa_tl / features }
                              \l__osgstyler_font_load_features_tl
                            \exp_args:NNVV \fontspec_gset_family:Nnn
                              \l__osgstyler_font_load_family_tl
                              \l__osgstyler_font_load_features_tl
                              \l__osgstyler_font_load_name_tl
                            \prop_gput:NeV \g__osgstyler_font_family_prop
                              { #1 / \l_tmpa_tl }
                              \l__osgstyler_font_load_family_tl
                          }
                      }
                  }
              }
          }
          { \msg_error:nnn { osgstyler } { unknown-font-palette } {#1} }
      }
  }

\NewDocumentCommand \UseOsgFontPalette { m }
  { \__osgstyler_font_palette_use:n {#1} }

\NewExpandableDocumentCommand \OsgActiveFontPalette { }
  { \tl_use:N \g__osgstyler_font_palette_tl }

\cs_new_protected:Npn \__osgstyler_font_select:n #1
  {
    \bool_if:NTF \g__osgstyler_colors_only_bool
      { \msg_error:nn { osgstyler } { font-management-unavailable } }
      {
        \str_if_eq:nnTF {#1}{math}
          { \msg_error:nn { osgstyler } { math-font-selection } }
          {
            \tl_if_empty:NTF \g__osgstyler_font_palette_tl
              { \msg_error:nnn { osgstyler } { unknown-font-palette } { } }
              {
                \exp_args:NV \__osgstyler_font_slot_resolve:nnN
                  \g__osgstyler_font_palette_tl {#1} \l_tmpa_tl
                \prop_get:NeN \g__osgstyler_font_family_prop
                  { \g__osgstyler_font_palette_tl / \l_tmpa_tl } \l_tmpb_tl
                \fontfamily{\l_tmpb_tl}\selectfont
              }
          }
      }
  }

\NewDocumentCommand \UseOsgFont { m }
  { \__osgstyler_font_select:n {#1} }

\DeclareOsgFontPalette { osgstyler-default }
  {
    main      = { font = { Latin Modern Roman } },
    companion = { font = { Latin Modern Sans } },
    display   = { alias = companion },
    accent    = { alias = companion },
    mono      = { font = { Latin Modern Mono } },
    symbol    = { alias = main }
  }
\bool_if:NF \g__osgstyler_colors_only_bool
  { \UseOsgFontPalette { osgstyler-default } }

% \ldeen*{Begleiten @1/@2 aus Konsistenzgründen (siehe dort): @3 bleibt
% wie im Default bei \cls*{amsthm}-nahen Dokumenten üblich; @4 tauscht
% nur die Begleitschrift (\code{companion}/\code{accent}/\code{display})
% gegen eine deutlicher zeitgenössisch wirkende Groteske, ohne den
% Fließtext (\code{main}) anzutasten -- ein bewusst kleiner, risikoarmer
% Unterschied.}
% {Accompany @1/@2 for consistency (see there): @3 stays exactly like
% the default, as usual for \cls*{amsthm}-adjacent documents; @4 only
% swaps the companion face (\code{companion}/\code{accent}/\code{display})
% for a visibly more contemporary sans, leaving body text (\code{main})
% untouched -- a deliberately small, low-risk difference.}
% {\code{osl-standard}}{\code{osl-modern}}{osl-standard}
\DeclareOsgFontPalette { osl-standard }
  {
    main      = { font = { Latin Modern Roman } },
    companion = { font = { Latin Modern Sans } },
    display   = { alias = companion },
    accent    = { alias = companion },
    mono      = { font = { Latin Modern Mono } },
    symbol    = { alias = main }
  }
\DeclareOsgFontPalette { osl-modern }
  {
    main      = { font = { Latin Modern Roman } },
    companion = { font = { TeX Gyre Heros } },
    display   = { alias = companion },
    accent    = { alias = companion },
    mono      = { font = { Latin Modern Mono } },
    symbol    = { alias = main }
  }

% Color palettes deliberately mirror lttheme's public color vocabulary and
% value syntax.  osgstyler owns neutral, stable color names; a presentation
% adapter can copy these colors without making the general package depend on
% lttheme.
\clist_const:Nn \c__osgstyler_color_slots_clist
  { primary, secondary, accent, text, background, structure, alert, example }

\prop_new:N \g__osgstyler_color_palette_prop
\tl_new:N \g__osgstyler_color_palette_tl
\seq_new:N \l__osgstyler_color_resolution_seq
\tl_new:N \l__osgstyler_color_resolution_model_tl
\tl_new:N \l__osgstyler_color_resolution_target_tl
\tl_new:N \l__osgstyler_color_resolution_discard_tl
\tl_new:N \l__osgstyler_color_name_tl

\msg_new:nnnn { osgstyler } { unknown-color-slot }
  { Unknown~color~palette~slot~'#1'. }
  { The~portable~slots~are~primary,~secondary,~accent,~text,~background,~structure,~alert,~and~example. }
\msg_new:nnnn { osgstyler } { missing-primary-color }
  { Color~palette~'#1'~does~not~define~the~required~slot~'primary'. }
  { Add~primary={model=...,~value=...}~to~the~palette. }
\msg_new:nnnn { osgstyler } { unknown-color-palette }
  { Unknown~color~palette~'#1'. }
  { Declare~it~with~\token_to_str:N\DeclareOsgColorPalette~before~using~it. }
\msg_new:nnnn { osgstyler } { color-alias-cycle }
  { Color~palette~'#1'~contains~an~alias~cycle~at~slot~'#2'. }
  { Change~the~aliases~so~that~they~eventually~resolve~to~a~color~resource. }
\msg_new:nnnn { osgstyler } { invalid-color-alias }
  { Color~slot~'#2'~in~palette~'#1'~aliases~unknown~slot~'#3'. }
  { Use~one~of~the~portable~color~slots~as~the~alias~target. }
\msg_new:nnnn { osgstyler } { incomplete-color-resource }
  { Color~slot~'#2'~in~palette~'#1'~does~not~specify~both~model~and~value,~or~an~alias. }
  { Use~model=...,~value=...~for~a~resource~or~alias=...~for~an~alias. }
\msg_new:nnnn { osgstyler } { conflicting-color-resource }
  { Color~slot~'#2'~in~palette~'#1'~specifies~both~a~resource~and~an~alias. }
  { A~slot~must~either~own~a~color~resource~or~alias~another~slot. }

\tl_new:N \l__osgstyler_color_model_tl
\tl_new:N \l__osgstyler_color_value_tl
\tl_new:N \l__osgstyler_color_alias_tl
\keys_define:nn { osgstyler / color-resource }
  {
    model .tl_set:N = \l__osgstyler_color_model_tl,
    value .tl_set:N = \l__osgstyler_color_value_tl,
    alias .tl_set:N = \l__osgstyler_color_alias_tl,
  }

\cs_new_protected:Npn \__osgstyler_color_resource_store:nnn #1#2#3
  {
    \tl_clear:N \l__osgstyler_color_model_tl
    \tl_clear:N \l__osgstyler_color_value_tl
    \tl_clear:N \l__osgstyler_color_alias_tl
    \keys_set:nn { osgstyler / color-resource } {#3}
    \bool_lazy_and:nnTF
      { ! \tl_if_empty_p:N \l__osgstyler_color_alias_tl }
      {
        \bool_lazy_or_p:nn
          { ! \tl_if_empty_p:N \l__osgstyler_color_model_tl }
          { ! \tl_if_empty_p:N \l__osgstyler_color_value_tl }
      }
      { \msg_error:nnnn { osgstyler } { conflicting-color-resource } {#1} {#2} }
      {
        \tl_if_empty:NTF \l__osgstyler_color_alias_tl
          {
            \bool_lazy_and:nnTF
              { ! \tl_if_empty_p:N \l__osgstyler_color_model_tl }
              { ! \tl_if_empty_p:N \l__osgstyler_color_value_tl }
              {
                \prop_gput:Nnn \g__osgstyler_color_palette_prop
                  { #1 / #2 / kind } { resource }
                \prop_gput:NnV \g__osgstyler_color_palette_prop
                  { #1 / #2 / model } \l__osgstyler_color_model_tl
                \prop_gput:NnV \g__osgstyler_color_palette_prop
                  { #1 / #2 / value } \l__osgstyler_color_value_tl
              }
              { \msg_error:nnnn { osgstyler } { incomplete-color-resource } {#1} {#2} }
          }
          {
            \clist_if_in:NVTF \c__osgstyler_color_slots_clist
              \l__osgstyler_color_alias_tl
              {
                \prop_gput:Nnn \g__osgstyler_color_palette_prop
                  { #1 / #2 / kind } { alias }
                \prop_gput:NnV \g__osgstyler_color_palette_prop
                  { #1 / #2 / alias } \l__osgstyler_color_alias_tl
              }
              { \msg_error:nneee { osgstyler } { invalid-color-alias }
                  {#1}{#2} \l__osgstyler_color_alias_tl }
          }
      }
  }

\cs_new_protected:Npn \__osgstyler_color_palette_declare:nn #1#2
  {
    \clist_map_inline:Nn \c__osgstyler_color_slots_clist
      {
        \clist_map_inline:nn { kind, model, value, alias }
          { \prop_gremove:Nn \g__osgstyler_color_palette_prop
              { #1 / ##1 / ####1 } }
        \keys_define:nn { osgstyler / color-palette / #1 }
          { ##1 .code:n = \__osgstyler_color_resource_store:nnn {#1}{##1}{####1} }
      }
    \prop_gremove:Nn \g__osgstyler_color_palette_prop { #1 / declared }
    \keys_set:nn { osgstyler / color-palette / #1 } {#2}
    \prop_if_in:NnF \g__osgstyler_color_palette_prop { #1 / primary / kind }
      { \msg_error:nnn { osgstyler } { missing-primary-color } {#1} }
    \prop_gput:Nnn \g__osgstyler_color_palette_prop { #1 / declared } { true }
  }

\NewDocumentCommand \DeclareOsgColorPalette { m m }
  { \__osgstyler_color_palette_declare:nn {#1}{#2} }

% Ordered color series complement semantic palettes where position, rather
% than a portable role name, identifies a color.  They deliberately reuse
% the color-resource syntax, but do not support aliases: an integer alias
% would make reordering surprisingly change its meaning.
\prop_new:N \g__osgstyler_color_series_prop
\int_new:N \l__osgstyler_color_series_index_int

\msg_new:nnnn { osgstyler } { invalid-color-series-resource }
  { Color~series~'#1',~item~#2~is~not~a~complete~color~resource. }
  { Each~item~must~specify~model=...~and~value=...;~aliases~are~not~supported. }

\cs_new:Npn \__osgstyler_color_series_name:nn #1#2
  { osgstyler-series-#1-#2 }

\cs_new_protected:Npn \__osgstyler_color_series_item_store:nnn #1#2#3
  {
    \tl_clear:N \l__osgstyler_color_model_tl
    \tl_clear:N \l__osgstyler_color_value_tl
    \tl_clear:N \l__osgstyler_color_alias_tl
    \keys_set:nn { osgstyler / color-resource } {#3}
    \bool_lazy_all:nTF
      {
        { ! \tl_if_empty_p:N \l__osgstyler_color_model_tl }
        { ! \tl_if_empty_p:N \l__osgstyler_color_value_tl }
        { \tl_if_empty_p:N \l__osgstyler_color_alias_tl }
      }
      {
        \prop_gput:NnV \g__osgstyler_color_series_prop
          { #1 / #2 / model } \l__osgstyler_color_model_tl
        \prop_gput:NnV \g__osgstyler_color_series_prop
          { #1 / #2 / value } \l__osgstyler_color_value_tl
        \tl_set:Ne \l__osgstyler_color_name_tl
          { \__osgstyler_color_series_name:nn {#1}{#2} }
        \exp_args:NVVV \color_set:nnn \l__osgstyler_color_name_tl
          \l__osgstyler_color_model_tl \l__osgstyler_color_value_tl
        \exp_args:Nooo \definecolor { \l__osgstyler_color_name_tl }
          { \l__osgstyler_color_model_tl } { \l__osgstyler_color_value_tl }
      }
      { \msg_error:nnnn { osgstyler } { invalid-color-series-resource } {#1}{#2} }
  }

\cs_new_protected:Npn \__osgstyler_color_series_declare:nn #1#2
  {
    \int_zero:N \l__osgstyler_color_series_index_int
    \clist_map_inline:nn {#2}
      {
        \int_incr:N \l__osgstyler_color_series_index_int
        \exp_args:NnV \__osgstyler_color_series_item_store:nnn {#1}
          \l__osgstyler_color_series_index_int {##1}
      }
    \prop_gput:Nnx \g__osgstyler_color_series_prop { #1 / count }
      { \int_use:N \l__osgstyler_color_series_index_int }
  }

\NewDocumentCommand \DeclareOsgColorSeries { m m }
  { \__osgstyler_color_series_declare:nn {#1}{#2} }

\NewExpandableDocumentCommand \OsgColorSeriesCount { m }
  { \prop_item:Nn \g__osgstyler_color_series_prop { #1 / count } }

\NewExpandableDocumentCommand \OsgColorSeriesColorName { m m }
  { \__osgstyler_color_series_name:nn {#1}{#2} }

\int_new:N \l__osgstyler_color_series_generate_count_int
\tl_new:N \l__osgstyler_color_series_generate_primary_tl
\tl_new:N \l__osgstyler_color_series_generate_accent_tl
\tl_new:N \l__osgstyler_color_series_generate_background_tl
\tl_new:N \l__osgstyler_color_series_generated_tl
\tl_new:N \l__osgstyler_color_series_colorspec_tl

\keys_define:nn { osgstyler / generate-color-series }
  {
    count      .int_set:N = \l__osgstyler_color_series_generate_count_int,
    primary    .tl_set:N  = \l__osgstyler_color_series_generate_primary_tl,
    accent     .tl_set:N  = \l__osgstyler_color_series_generate_accent_tl,
    background .tl_set:N  = \l__osgstyler_color_series_generate_background_tl,
  }

\msg_new:nnn { osgstyler } { luatex-required-for-series-generation }
  { GenerateOsgColorSeries~requires~LuaLaTeX. }

\cs_new_protected:Npn \__osgstyler_colorspec_to_html:nnN #1#2#3
  { \convertcolorspec{#1}{#2}{HTML}#3 }
\cs_new_protected:Npn \__osgstyler_color_to_html:nN #1#2
  {
    \extractcolorspec{#1}\l__osgstyler_color_series_colorspec_tl
    \exp_after:wN \__osgstyler_colorspec_to_html:nnN
      \l__osgstyler_color_series_colorspec_tl #2
  }

\sys_if_engine_luatex:T
  { \directlua{ osgstyler = require("osgstyler") } }

\cs_new_protected:Npn \__osgstyler_color_series_generate:nn #1#2
  {
    \keys_set:nn { osgstyler / generate-color-series }
      {
        count=8,
        primary=\OsgColorName{primary},
        accent=\OsgColorName{accent},
        background=\OsgColorName{background},
        #2
      }
    \int_set:Nn \l__osgstyler_color_series_generate_count_int
      {
        \int_max:nn { 2 }
          { \int_min:nn { 32 } { \l__osgstyler_color_series_generate_count_int } }
      }
    \sys_if_engine_luatex:TF
      {
        \exp_args:NV \__osgstyler_color_to_html:nN
          \l__osgstyler_color_series_generate_primary_tl
          \l__osgstyler_color_series_generate_primary_tl
        \exp_args:NV \__osgstyler_color_to_html:nN
          \l__osgstyler_color_series_generate_accent_tl
          \l__osgstyler_color_series_generate_accent_tl
        \exp_args:NV \__osgstyler_color_to_html:nN
          \l__osgstyler_color_series_generate_background_tl
          \l__osgstyler_color_series_generate_background_tl
        \tl_set:Nx \l__osgstyler_color_series_generated_tl
          {
            \directlua
              {
                tex.sprint(osgstyler.generate_color_series(
                  "\luaescapestring{\l__osgstyler_color_series_generate_primary_tl}",
                  "\luaescapestring{\l__osgstyler_color_series_generate_accent_tl}",
                  "\luaescapestring{\l__osgstyler_color_series_generate_background_tl}",
                  \int_use:N \l__osgstyler_color_series_generate_count_int))
              }
          }
        \exp_args:NnV \DeclareOsgColorSeries {#1}
          \l__osgstyler_color_series_generated_tl
      }
      { \msg_error:nn { osgstyler } { luatex-required-for-series-generation } }
  }

\NewDocumentCommand \GenerateOsgColorSeries { m O{} }
  { \__osgstyler_color_series_generate:nn {#1}{#2} }

\cs_new:Npn \__osgstyler_color_fallback:n #1
  {
    \str_case:nnF {#1}
      {
        { secondary } { primary }
        { accent }    { secondary }
        { structure } { primary }
        { alert }     { primary }
        { example }   { primary }
      }
      { }
  }

% text/background are not aliased into the slot-fallback chain above:
% every other slot's fallback is another *theme* color (ultimately
% primary), which is appropriate for accents but not for body text or
% the page background, where a theme accent color risks poor
% contrast or an unintended colored background. Their fallback is
% therefore a fixed neutral value, kept in its own small table rather
% than special-cased in the resolution code.
\prop_new:N \c__osgstyler_color_absolute_default_prop
\prop_gput:Nnn \c__osgstyler_color_absolute_default_prop { text } { black }
\prop_gput:Nnn \c__osgstyler_color_absolute_default_prop { background } { white }

\cs_new_protected:Npn \__osgstyler_color_resolve:nnN #1#2#3
  {
    \seq_if_in:NnTF \l__osgstyler_color_resolution_seq {#2}
      {
        \msg_error:nnnn { osgstyler } { color-alias-cycle } {#1}{#2}
        \tl_clear:N #3
      }
      {
        \seq_put_right:Nn \l__osgstyler_color_resolution_seq {#2}
        \prop_get:NnNTF \g__osgstyler_color_palette_prop
          { #1 / #2 / kind } \l__osgstyler_color_resolution_model_tl
          {
            \str_if_eq:VnTF \l__osgstyler_color_resolution_model_tl { resource }
              { \tl_set:Nn #3 {#2} }
              {
                \prop_get:NnN \g__osgstyler_color_palette_prop
                  { #1 / #2 / alias } \l__osgstyler_color_resolution_target_tl
                \exp_args:NnV \__osgstyler_color_resolve:nnN {#1}
                  \l__osgstyler_color_resolution_target_tl #3
              }
          }
          {
            \tl_set:Ne \l__osgstyler_color_resolution_target_tl
              { \__osgstyler_color_fallback:n {#2} }
            \tl_if_empty:NTF \l__osgstyler_color_resolution_target_tl
              { \tl_clear:N #3 }
              { \exp_args:NnV \__osgstyler_color_resolve:nnN {#1}
                  \l__osgstyler_color_resolution_target_tl #3 }
          }
        \seq_pop_right:NN \l__osgstyler_color_resolution_seq
          \l__osgstyler_color_resolution_discard_tl
      }
  }

\cs_new_protected:Npn \__osgstyler_color_slot_resolve:nnN #1#2#3
  {
    \clist_if_in:NnTF \c__osgstyler_color_slots_clist {#2}
      {
        \seq_clear:N \l__osgstyler_color_resolution_seq
        \__osgstyler_color_resolve:nnN {#1}{#2} #3
      }
      {
        \msg_error:nnn { osgstyler } { unknown-color-slot } {#2}
        \tl_clear:N #3
      }
  }

\NewDocumentCommand \OsgColorPaletteResolvedSlotTo { m m m }
  { \__osgstyler_color_slot_resolve:nnN {#1}{#2} #3 }

\NewExpandableDocumentCommand \OsgColorPaletteValue { m m m }
  { \prop_item:Nn \g__osgstyler_color_palette_prop { #1 / #2 / #3 } }

\cs_new:Npn \__osgstyler_color_name:n #1 { osgstyler-#1 }

\NewExpandableDocumentCommand \OsgColorName { m }
  { \__osgstyler_color_name:n {#1} }

% \ldeen*{@1 schreibt die aufgelöste Farbe sowohl nativ über @2 als
% auch klassisch über @3: \cls*{ltx-talk} und vergleichbare,
% modernisierte Klassen ersetzen \cs{color} durch eine auf @2s eigenem
% Modul aufbauende Fassung, die nur nativ registrierte Farben kennt;
% Werkzeuge wie \cs{extractcolorspec} und unveränderte Klassen kennen
% dagegen nur die klassische Registrierung -- nur die doppelte
% Schreibweise bedient beide Seiten zuverlässig. Ein dritter Parameter
% trennt bewusst "`welcher Slot liefert die Daten"' (\#2, nach
% Alias-Auflösung der eigentliche Ressourcen-Besitzer) von "`unter
% welchem Namen wird geschrieben"' (\#3, der ursprünglich angefragte
% Slot): Ein Alias-Slot bekommt dadurch seine eigene Farbe direkt
% zugewiesen, statt sie nachträglich unter @4 auf die Besitzer-Farbe zu
% verweisen -- @4 selbst ist, anders als @2/@3, grundsätzlich nur lokal
% gültig und würde eine Zuweisung aus der Deklarationszeit nicht
% zuverlässig bis zum Dokumentkörper erhalten.}{@1 writes the resolved
% color both natively via @2 and classically via @3: \cls*{ltx-talk} and
% similarly modernized classes replace \cs{color} with a version built
% on @2's own module, which only recognizes natively registered colors;
% tools such as \cs{extractcolorspec} and unmodified classes, in turn,
% only recognize the classical registration -- only writing both
% reliably serves either side. A third parameter deliberately separates
% "`which slot supplies the data"' (\#2, the actual resource owner after
% alias resolution) from "`under which name is it written"' (\#3, the
% originally requested slot): an alias slot thus gets its own color
% assigned directly, instead of pointing it at the owner's color
% afterward via @4 -- @4 itself, unlike @2/@3, is fundamentally
% local-only and would not reliably carry an assignment made at
% declaration time through to the document body.}
% {\cs{color\_set:nnn}}{\pkg*{l3color}}{\cs{definecolor}}{\cs{color\_set\_eq:nn}}
\cs_new_protected:Npn \__osgstyler_color_define_from_resource:nnn #1#2#3
  {
    \prop_get:NnN \g__osgstyler_color_palette_prop
      { #1 / #2 / model } \l__osgstyler_color_resolution_model_tl
    \prop_get:NnN \g__osgstyler_color_palette_prop
      { #1 / #2 / value } \l__osgstyler_color_resolution_target_tl
    \tl_set:Nx \l__osgstyler_color_name_tl
      { \__osgstyler_color_name:n {#3} }
    \color_set:VVV
      \l__osgstyler_color_name_tl
      \l__osgstyler_color_resolution_model_tl
      \l__osgstyler_color_resolution_target_tl
    \use:e
      {
        \exp_not:N \definecolor
          { \l__osgstyler_color_name_tl }
          { \l__osgstyler_color_resolution_model_tl }
          { \l__osgstyler_color_resolution_target_tl }
      }
  }
\cs_generate_variant:Nn \__osgstyler_color_define_from_resource:nnn { nVn }

\cs_new_protected:Npn \__osgstyler_color_palette_use:n #1
  {
    \prop_if_in:NnTF \g__osgstyler_color_palette_prop { #1 / declared }
      {
        \tl_gset:Nn \g__osgstyler_color_palette_tl {#1}
        \clist_map_inline:Nn \c__osgstyler_color_slots_clist
          {
            \__osgstyler_color_slot_resolve:nnN {#1}{##1} \l_tmpa_tl
            \tl_if_empty:NTF \l_tmpa_tl
              {
                \prop_if_in:NnT \c__osgstyler_color_absolute_default_prop {##1}
                  {
                    \prop_get:NnN \c__osgstyler_color_absolute_default_prop
                      {##1} \l_tmpb_tl
                    \tl_set:Nx \l_tmpc_tl { \__osgstyler_color_name:n {##1} }
                    \str_case:VnF \l_tmpb_tl
                      {
                        { black }
                          {
                            \exp_args:NV \color_set:nnn \l_tmpc_tl { gray } { 0 }
                            \exp_args:Nee \colorlet { \l_tmpc_tl } { black }
                          }
                        { white }
                          {
                            \exp_args:NV \color_set:nnn \l_tmpc_tl { gray } { 1 }
                            \exp_args:Nee \colorlet { \l_tmpc_tl } { white }
                          }
                      }
                      { }
                  }
              }
              {
                \__osgstyler_color_define_from_resource:nVn
                  {#1} \l_tmpa_tl {##1}
              }
          }
      }
      { \msg_error:nnn { osgstyler } { unknown-color-palette } {#1} }
  }

\NewDocumentCommand \UseOsgColorPalette { m }
  { \__osgstyler_color_palette_use:n {#1} }

\NewExpandableDocumentCommand \OsgActiveColorPalette { }
  { \tl_use:N \g__osgstyler_color_palette_tl }

\DeclareOsgColorPalette { osgstyler-default }
  {
    primary    = { model = RGB, value = {43,58,103} },
    secondary  = { model = RGB, value = {73,106,129} },
    accent     = { model = RGB, value = {102,153,155} },
    text       = { model = RGB, value = {51,51,51} },
    background = { model = RGB, value = {255,255,255} },
    structure  = { alias = primary },
    alert      = { model = RGB, value = {176,0,32} },
    example    = { model = RGB, value = {22,101,52} }
  }
\UseOsgColorPalette { osgstyler-default }

% \ldeen*{@1/@2 sind fertige, aber nicht automatisch aktive Paletten
% (analog zu @3s Themes für Beamer): eine für die Langform gedachte,
% zurückhaltende Variante nah am unkolorierten \cls*{amsthm}-Erscheinungsbild,
% und eine an einem verbreiteten, aufeinander abgestimmten modernen
% Farbsystem orientierte. Ein Dokument aktiviert eine davon bewusst über
% \cs{UseOsgColorPalette}; ungenutzt ändert sich nichts.}
% {@1/@2 are ready-made but not automatically active palettes (mirroring
% @3's themes for beamer): one intended for the long form, restrained
% and close to unadorned \cls*{amsthm} appearance, and one modeled on a
% widely used, coordinated modern color system. A document activates
% one deliberately via \cs{UseOsgColorPalette}; unused, nothing
% changes.}
% {\code{osl-standard}}{\code{osl-modern}}{\pkg*{lttheme}}
\DeclareOsgColorPalette { osl-standard }
  {
    primary    = { model = HTML, value = 141E3C },
    secondary  = { model = HTML, value = 505050 },
    accent     = { model = HTML, value = C8C8C8 },
    text       = { model = HTML, value = 000000 },
    background = { model = HTML, value = FFFFFF },
    structure  = { alias = primary },
    alert      = { model = HTML, value = B40000 },
    example    = { model = HTML, value = 006400 }
  }
\DeclareOsgColorPalette { osl-modern }
  {
    primary    = { model = HTML, value = 4338CA },
    secondary  = { model = HTML, value = 475569 },
    accent     = { model = HTML, value = F59E0B },
    text       = { model = HTML, value = 0F172A },
    background = { model = HTML, value = FFFFFF },
    structure  = { alias = primary },
    alert      = { model = HTML, value = E11D48 },
    example    = { model = HTML, value = 059669 }
  }

% Metric palettes provide a small, portable scale for layout and decoration
% backends.  Every declaration starts from complete defaults and may override
% only the values relevant to a theme.
\clist_const:Nn \c__osgstyler_metric_categories_clist
  { spacing, padding, rule, radius }
\prop_new:N \g__osgstyler_metric_palette_prop
\tl_new:N \g__osgstyler_metric_palette_tl
\tl_new:N \l__osgstyler_metric_palette_tl
\tl_new:N \l__osgstyler_metric_category_tl

\msg_new:nnnn { osgstyler } { unknown-metric-palette }
  { Unknown~metric~palette~'#1'. }
  { Declare~it~with~\token_to_str:N\DeclareOsgMetricPalette~before~using~it. }

\cs_new_protected:Npn \__osgstyler_metric_put:nn #1#2
  {
    \prop_gput:Nee \g__osgstyler_metric_palette_prop
      {
        \l__osgstyler_metric_palette_tl /
        \l__osgstyler_metric_category_tl / #1
      }
      { \exp_not:n {#2} }
  }

\keys_define:nn { osgstyler / metric-palette / spacing }
  {
    none .code:n = \__osgstyler_metric_put:nn { none }{#1},
    xs   .code:n = \__osgstyler_metric_put:nn { xs }{#1},
    sm   .code:n = \__osgstyler_metric_put:nn { sm }{#1},
    md   .code:n = \__osgstyler_metric_put:nn { md }{#1},
    lg   .code:n = \__osgstyler_metric_put:nn { lg }{#1},
    xl   .code:n = \__osgstyler_metric_put:nn { xl }{#1}
  }
\keys_define:nn { osgstyler / metric-palette / padding }
  {
    none    .code:n = \__osgstyler_metric_put:nn { none }{#1},
    compact .code:n = \__osgstyler_metric_put:nn { compact }{#1},
    normal  .code:n = \__osgstyler_metric_put:nn { normal }{#1},
    roomy   .code:n = \__osgstyler_metric_put:nn { roomy }{#1}
  }
\keys_define:nn { osgstyler / metric-palette / rule }
  {
    hairline .code:n = \__osgstyler_metric_put:nn { hairline }{#1},
    thin     .code:n = \__osgstyler_metric_put:nn { thin }{#1},
    normal   .code:n = \__osgstyler_metric_put:nn { normal }{#1},
    thick    .code:n = \__osgstyler_metric_put:nn { thick }{#1}
  }
\keys_define:nn { osgstyler / metric-palette / radius }
  {
    square .code:n = \__osgstyler_metric_put:nn { square }{#1},
    small  .code:n = \__osgstyler_metric_put:nn { small }{#1},
    medium .code:n = \__osgstyler_metric_put:nn { medium }{#1},
    round  .code:n = \__osgstyler_metric_put:nn { round }{#1}
  }

\cs_new_protected:Npn \__osgstyler_metric_category:nn #1#2
  {
    \tl_set:Nn \l__osgstyler_metric_category_tl {#1}
    \keys_set:nn { osgstyler / metric-palette / #1 } {#2}
  }
\keys_define:nn { osgstyler / metric-palette }
  {
    spacing .code:n = \__osgstyler_metric_category:nn { spacing }{#1},
    padding .code:n = \__osgstyler_metric_category:nn { padding }{#1},
    rule    .code:n = \__osgstyler_metric_category:nn { rule }{#1},
    radius  .code:n = \__osgstyler_metric_category:nn { radius }{#1}
  }

\cs_new_protected:Npn \__osgstyler_metric_defaults:
  {
    \__osgstyler_metric_category:nn { spacing }
      { none=0pt, xs=.25\baselineskip, sm=.5\baselineskip,
        md=1\baselineskip, lg=1.5\baselineskip, xl=2\baselineskip }
    \__osgstyler_metric_category:nn { padding }
      { none=0pt, compact=.35em, normal=.65em, roomy=1em }
    \__osgstyler_metric_category:nn { rule }
      { hairline=.2pt, thin=.4pt, normal=.7pt, thick=1.2pt }
    \__osgstyler_metric_category:nn { radius }
      { square=0pt, small=2pt, medium=4pt, round=6pt }
  }

\cs_new_protected:Npn \__osgstyler_metric_palette_declare:nn #1#2
  {
    \tl_set:Nn \l__osgstyler_metric_palette_tl {#1}
    \__osgstyler_metric_defaults:
    \keys_set:nn { osgstyler / metric-palette } {#2}
    \prop_gput:Nnn \g__osgstyler_metric_palette_prop
      { #1 / declared } { true }
  }

\NewDocumentCommand \DeclareOsgMetricPalette { m m }
  { \__osgstyler_metric_palette_declare:nn {#1}{#2} }

\NewDocumentCommand \UseOsgMetricPalette { m }
  {
    \prop_if_in:NnTF \g__osgstyler_metric_palette_prop { #1 / declared }
      { \tl_gset:Nn \g__osgstyler_metric_palette_tl {#1} }
      { \msg_error:nnn { osgstyler } { unknown-metric-palette } {#1} }
  }

\NewExpandableDocumentCommand \OsgMetricPaletteValue { m m m }
  { \prop_item:Nn \g__osgstyler_metric_palette_prop { #1 / #2 / #3 } }

\NewExpandableDocumentCommand \OsgMetric { m m }
  {
    \prop_item:Ne \g__osgstyler_metric_palette_prop
      { \g__osgstyler_metric_palette_tl / #1 / #2 }
  }

\NewExpandableDocumentCommand \OsgActiveMetricPalette { }
  { \tl_use:N \g__osgstyler_metric_palette_tl }

\DeclareOsgMetricPalette { osgstyler-default } { }
\UseOsgMetricPalette { osgstyler-default }

% \ldeen*{Anders als Farb-/Font-/Metrikpaletten ist eine Listenpalette
% keine reine Datenablage: @1 wendet die hinterlegten Optionen sofort
% über @2 an, denn genau das ist auch @2s eigenes Anwendungsmodell
% (gilt ab dem Aufruf, nicht rückwirkend). Werte pro Ebene sind
% rohe @2-Optionen -- dieses Paket erfindet @2s Vokabular nicht neu,
% es organisiert es nur unter einem Palettennamen und macht die
% Anwendung modusabhängig (siehe unten). Nicht deklarierte
% Ebenen/Listenarten bleiben unangetastet.}
% {Unlike color/font/metric palettes, a list palette is not a pure data
% store: @1 applies the stored options immediately via @2, since that
% matches @2's own application model (takes effect from the call
% onward, not retroactively). Values per level are raw @2 options --
% this package does not reinvent @2's vocabulary, it only organizes it
% under a palette name and makes applying it mode-aware (see below).
% Levels/list types that were never declared stay untouched.}
% {\cs{UseOsgListPalette}}{\pkg*{enumitem}}

\prop_new:N \g__osgstyler_list_palette_prop
\tl_new:N \l__osgstyler_list_palette_tl
\tl_new:N \l__osgstyler_list_type_tl
\tl_new:N \l__osgstyler_list_value_tl

\msg_new:nnnn { osgstyler } { unknown-list-palette }
  { Unknown~list~palette~'#1'. }
  { Declare~it~with~\token_to_str:N\DeclareOsgListPalette~before~using~it. }

\cs_new_protected:Npn \__osgstyler_list_put:nn #1#2
  {
    \prop_gput:Nee \g__osgstyler_list_palette_prop
      {
        \l__osgstyler_list_palette_tl /
        \l__osgstyler_list_type_tl / #1
      }
      { \exp_not:n {#2} }
  }

\keys_define:nn { osgstyler / list-palette / type }
  {
    1 .code:n = \__osgstyler_list_put:nn { 1 }{#1},
    2 .code:n = \__osgstyler_list_put:nn { 2 }{#1},
    3 .code:n = \__osgstyler_list_put:nn { 3 }{#1},
    4 .code:n = \__osgstyler_list_put:nn { 4 }{#1}
  }

\cs_new_protected:Npn \__osgstyler_list_type:nn #1#2
  {
    \tl_set:Nn \l__osgstyler_list_type_tl {#1}
    \keys_set:nn { osgstyler / list-palette / type } {#2}
  }
\keys_define:nn { osgstyler / list-palette }
  {
    itemize     .code:n = \__osgstyler_list_type:nn { itemize }{#1},
    enumerate   .code:n = \__osgstyler_list_type:nn { enumerate }{#1},
    description .code:n = \__osgstyler_list_type:nn { description }{#1}
  }

\cs_new_protected:Npn \__osgstyler_list_palette_declare:nn #1#2
  {
    \tl_set:Nn \l__osgstyler_list_palette_tl {#1}
    \keys_set:nn { osgstyler / list-palette } {#2}
    \prop_gput:Nnn \g__osgstyler_list_palette_prop { #1 / declared } { true }
  }

\NewDocumentCommand \DeclareOsgListPalette { m m }
  { \__osgstyler_list_palette_declare:nn {#1}{#2} }

\cs_new_protected:Npn \__osgstyler_list_apply_level:nnn #1#2#3
  {
    \prop_if_in:NnT \g__osgstyler_list_palette_prop { #1 / #2 / #3 }
      {
        % enumitem's own key=value scanner reads its options argument
        % before full expansion, so handing it a macro call there (even
        % one that expands to the right text) misparses -- \arabic*
        % style placeholders in particular. Building the whole \setlist
        % call as already-expanded literal text first, then executing
        % that, avoids the issue entirely.
        \tl_set:Ne \l__osgstyler_list_value_tl
          {
            \exp_not:N \setlist [ #2 , #3 ]
              {
                \prop_item:Nn \g__osgstyler_list_palette_prop
                  { #1 / #2 / #3 }
              }
          }
        \tl_use:N \l__osgstyler_list_value_tl
      }
  }

\cs_new_protected:Npn \__osgstyler_list_apply_type:nn #1#2
  {
    \clist_map_inline:nn { 1 , 2 , 3 , 4 }
      { \__osgstyler_list_apply_level:nnn {#1}{#2}{##1} }
  }

\cs_new_protected:Npn \__osgstyler_list_palette_apply:n #1
  {
    \__osgstyler_list_apply_type:nn {#1} { itemize }
    \__osgstyler_list_apply_type:nn {#1} { enumerate }
    \__osgstyler_list_apply_type:nn {#1} { description }
  }

\NewDocumentCommand \UseOsgListPalette { m }
  {
    \prop_if_in:NnTF \g__osgstyler_list_palette_prop { #1 / declared }
      { \__osgstyler_list_palette_apply:n {#1} }
      { \msg_error:nnn { osgstyler } { unknown-list-palette } {#1} }
  }

\DeclareOsgListPalette { osgstyler-default } { }
\UseOsgListPalette { osgstyler-default }

% \ldeen*{Ein bewusst zurückhaltendes Accessoire zu \code{osl-modern}
% (siehe dort): ein gezeichnetes \cs{rule}-Quadrat statt eines
% Font-Glyphen für die oberste Aufzählungsebene, gefärbt über
% \cs{OsgColorName}. Da ein gezeichnetes Element kein Glyph aus
% irgendeiner Schrift braucht, kann es auch nicht mit einer
% Begleitschrift kollidieren. \code{osl-standard} bleibt bei
% enumitems eigenen Vorgaben.}
% {A deliberately restrained accessory to \code{osl-modern} (see
% there): a drawn \cs{rule} square instead of a font glyph for the
% top-level itemize marker, colored via \cs{OsgColorName}. Since a
% drawn element needs no glyph from any typeface, it cannot clash with
% a companion face either. \code{osl-standard} stays with enumitem's
% own defaults.}
\DeclareOsgListPalette { osl-modern }
  {
    itemize =
      {
        1 = { label = \textcolor{\OsgColorName{accent}}
                { \rule[.15ex]{.9ex}{.9ex} } }
      }
  }

% Decorations are backend-neutral visual recipes.  A decoration may combine
% any number of components; rendering packages consume the stored values.
\clist_const:Nn \c__osgstyler_decoration_families_clist
  { line, background, enclosure, margin, affix }
\clist_const:Nn \c__osgstyler_decoration_line_properties_clist
  { position, pattern, color, thickness, offset }
\clist_const:Nn \c__osgstyler_decoration_background_properties_clist
  { color, opacity, shape }
\clist_const:Nn \c__osgstyler_decoration_enclosure_properties_clist
  { border-color, fill-color, pattern, thickness, radius, padding }
\clist_const:Nn \c__osgstyler_decoration_margin_properties_clist
  { side, color, pattern, thickness, offset }
\clist_const:Nn \c__osgstyler_decoration_affix_properties_clist
  { position, content, symbol, color, font, gap }

\prop_new:N \g__osgstyler_decoration_prop
\tl_new:N \l__osgstyler_decoration_name_tl
\tl_new:N \l__osgstyler_decoration_family_tl
\tl_new:N \l__osgstyler_decoration_instance_tl

\msg_new:nnnn { osgstyler } { unknown-decoration }
  { Unknown~decoration~'#1'. }
  { Declare~it~with~\token_to_str:N\DeclareOsgDecoration~before~using~it. }
\msg_new:nnnn { osgstyler } { unknown-decoration-family }
  { Unknown~decoration~family~'#1'. }
  { The~families~are~line,~background,~enclosure,~margin,~and~affix. }
\msg_new:nnnn { osgstyler } { invalid-decoration-color }
  { Decoration~'#1'~uses~unknown~color~slot~'#2'. }
  { Use~a~portable~color-palette~slot. }
\msg_new:nnnn { osgstyler } { invalid-decoration-font }
  { Decoration~'#1'~uses~unknown~font~slot~'#2'. }
  { Use~a~portable~font-palette~slot. }
\msg_new:nnnn { osgstyler } { conflicting-affix-content }
  { Affix~instance~'#2'~in~decoration~'#1'~has~both~content~and~symbol. }
  { Use~content={...}~for~literal~material~or~symbol=...~for~a~symbol~resource. }

\cs_new_protected:Npn \__osgstyler_decoration_instance_register:
  {
    \prop_get:NeNTF \g__osgstyler_decoration_prop
      {
        \l__osgstyler_decoration_name_tl /
        \l__osgstyler_decoration_family_tl / instances
      }
      \l_tmpa_tl
      { }
      { \tl_clear:N \l_tmpa_tl }
    \exp_args:NNV \clist_if_in:NnF
      \l_tmpa_tl \l__osgstyler_decoration_instance_tl
      {
        \exp_args:NNV \clist_put_right:Nn
          \l_tmpa_tl \l__osgstyler_decoration_instance_tl
        \prop_gput:Nee \g__osgstyler_decoration_prop
          {
            \l__osgstyler_decoration_name_tl /
            \l__osgstyler_decoration_family_tl / instances
          }
          { \l_tmpa_tl }
      }
    \prop_gput:Nee \g__osgstyler_decoration_prop
      {
        \l__osgstyler_decoration_name_tl /
        \l__osgstyler_decoration_family_tl /
        \l__osgstyler_decoration_instance_tl / present
      }
      { true }
  }

\cs_new_protected:Npn \__osgstyler_decoration_put:nn #1#2
  {
    \__osgstyler_decoration_instance_register:
    \prop_gput:Nee \g__osgstyler_decoration_prop
      {
        \l__osgstyler_decoration_name_tl /
        \l__osgstyler_decoration_family_tl /
        \l__osgstyler_decoration_instance_tl / #1
      }
      { \exp_not:n {#2} }
  }

\cs_new_protected:Npn \__osgstyler_decoration_color_put:nn #1#2
  {
    \clist_if_in:NnTF \c__osgstyler_color_slots_clist {#2}
      { \__osgstyler_decoration_put:nn {#1}{#2} }
      { \msg_error:nnxx { osgstyler } { invalid-decoration-color }
          { \l__osgstyler_decoration_name_tl } {#2} }
  }

\cs_new_protected:Npn \__osgstyler_decoration_font_put:n #1
  {
    \clist_if_in:NnTF \c__osgstyler_font_slots_clist {#1}
      { \__osgstyler_decoration_put:nn { font }{#1} }
      { \msg_error:nnxx { osgstyler } { invalid-decoration-font }
          { \l__osgstyler_decoration_name_tl } {#1} }
  }

\cs_new_protected:Npn \__osgstyler_decoration_affix_payload_put:nn #1#2
  {
    \__osgstyler_decoration_instance_register:
    \str_if_eq:nnTF {#1}{content}
      { \tl_set:Nn \l_tmpa_tl { symbol } }
      { \tl_set:Nn \l_tmpa_tl { content } }
    \prop_if_in:NeT \g__osgstyler_decoration_prop
      {
        \l__osgstyler_decoration_name_tl /
        \l__osgstyler_decoration_family_tl /
        \l__osgstyler_decoration_instance_tl / \l_tmpa_tl
      }
      {
        \msg_error:nnxx { osgstyler } { conflicting-affix-content }
          { \l__osgstyler_decoration_name_tl }
          { \l__osgstyler_decoration_instance_tl }
      }
    \__osgstyler_decoration_put:nn {#1}{#2}
  }

\keys_define:nn { osgstyler / decoration / line }
  {
    position  .code:n = \__osgstyler_decoration_put:nn { position }{#1},
    pattern   .code:n = \__osgstyler_decoration_put:nn { pattern }{#1},
    color     .code:n = \__osgstyler_decoration_color_put:nn { color }{#1},
    thickness .code:n = \__osgstyler_decoration_put:nn { thickness }{#1},
    offset    .code:n = \__osgstyler_decoration_put:nn { offset }{#1}
  }
\keys_define:nn { osgstyler / decoration / background }
  {
    color   .code:n = \__osgstyler_decoration_color_put:nn { color }{#1},
    opacity .code:n = \__osgstyler_decoration_put:nn { opacity }{#1},
    shape   .code:n = \__osgstyler_decoration_put:nn { shape }{#1}
  }
\keys_define:nn { osgstyler / decoration / enclosure }
  {
    border-color .code:n =
      \__osgstyler_decoration_color_put:nn { border-color }{#1},
    fill-color .code:n =
      \__osgstyler_decoration_color_put:nn { fill-color }{#1},
    pattern   .code:n = \__osgstyler_decoration_put:nn { pattern }{#1},
    thickness .code:n = \__osgstyler_decoration_put:nn { thickness }{#1},
    radius    .code:n = \__osgstyler_decoration_put:nn { radius }{#1},
    padding   .code:n = \__osgstyler_decoration_put:nn { padding }{#1}
  }
\keys_define:nn { osgstyler / decoration / margin }
  {
    side      .code:n = \__osgstyler_decoration_put:nn { side }{#1},
    color     .code:n = \__osgstyler_decoration_color_put:nn { color }{#1},
    pattern   .code:n = \__osgstyler_decoration_put:nn { pattern }{#1},
    thickness .code:n = \__osgstyler_decoration_put:nn { thickness }{#1},
    offset    .code:n = \__osgstyler_decoration_put:nn { offset }{#1}
  }
\keys_define:nn { osgstyler / decoration / affix }
  {
    position .choices:nn = { before, after }
      { \__osgstyler_decoration_put:nn { position }{#1} },
    content .code:n =
      \__osgstyler_decoration_affix_payload_put:nn { content }{#1},
    symbol .code:n =
      \__osgstyler_decoration_affix_payload_put:nn { symbol }{#1},
    color .code:n = \__osgstyler_decoration_color_put:nn { color }{#1},
    font  .code:n = \__osgstyler_decoration_font_put:n {#1},
    gap   .code:n = \__osgstyler_decoration_put:nn { gap }{#1}
  }

\cs_new_protected:Npn \__osgstyler_decoration_named_instance:n #1
  {
    \group_begin:
      \tl_set:NV \l__osgstyler_decoration_instance_tl \l_keys_key_str
      \exp_args:Ne \keys_set:nn
        { osgstyler / decoration / \l__osgstyler_decoration_family_tl }
        {#1}
    \group_end:
  }

\clist_map_inline:Nn \c__osgstyler_decoration_families_clist
  {
    \keys_define:nn { osgstyler / decoration / #1 }
      { unknown .code:n = \__osgstyler_decoration_named_instance:n {##1} }
  }

\cs_new_protected:Npn \__osgstyler_decoration_component:nn #1#2
  {
    \tl_set:Nn \l__osgstyler_decoration_family_tl {#1}
    \tl_set:Nn \l__osgstyler_decoration_instance_tl { default }
    \prop_gput:Nee \g__osgstyler_decoration_prop
      { \l__osgstyler_decoration_name_tl / #1 / present } { true }
    \keys_set:nn { osgstyler / decoration / #1 } {#2}
  }

\keys_define:nn { osgstyler / decoration }
  {
    line       .code:n = \__osgstyler_decoration_component:nn { line }{#1},
    background .code:n = \__osgstyler_decoration_component:nn { background }{#1},
    enclosure  .code:n = \__osgstyler_decoration_component:nn { enclosure }{#1},
    margin     .code:n = \__osgstyler_decoration_component:nn { margin }{#1},
    affix      .code:n = \__osgstyler_decoration_component:nn { affix }{#1}
  }

\cs_new_protected:Npn \__osgstyler_decoration_clear:n #1
  {
    \clist_map_inline:Nn \c__osgstyler_decoration_families_clist
      {
        \prop_gremove:Nn \g__osgstyler_decoration_prop { #1 / ##1 / present }
        \prop_get:NnNT \g__osgstyler_decoration_prop
          { #1 / ##1 / instances } \l_tmpa_tl
          {
            \clist_map_inline:Vn \l_tmpa_tl
              {
                \prop_gremove:Nn \g__osgstyler_decoration_prop
                  { #1 / ##1 / ####1 / present }
                \clist_map_inline:cn
                  { c__osgstyler_decoration_##1_properties_clist }
                  { \prop_gremove:Nn \g__osgstyler_decoration_prop
                      { #1 / ##1 / ####1 / ########1 } }
              }
          }
        \prop_gremove:Nn \g__osgstyler_decoration_prop
          { #1 / ##1 / instances }
      }
  }

\cs_new_protected:Npn \__osgstyler_decoration_declare:nn #1#2
  {
    \__osgstyler_decoration_clear:n {#1}
    \tl_set:Nn \l__osgstyler_decoration_name_tl {#1}
    \keys_set:nn { osgstyler / decoration } {#2}
    \prop_gput:Nnn \g__osgstyler_decoration_prop { #1 / declared } { true }
  }

\NewDocumentCommand \DeclareOsgDecoration { m m }
  { \__osgstyler_decoration_declare:nn {#1}{#2} }

\NewExpandableDocumentCommand \OsgDecorationValue { m m m }
  { \prop_item:Nn \g__osgstyler_decoration_prop { #1 / #2 / default / #3 } }

\NewExpandableDocumentCommand \OsgDecorationInstanceValue { m m m m }
  { \prop_item:Nn \g__osgstyler_decoration_prop { #1 / #2 / #3 / #4 } }

\NewExpandableDocumentCommand \OsgDecorationInstances { m m }
  { \prop_item:Nn \g__osgstyler_decoration_prop { #1 / #2 / instances } }

\prg_new_conditional:Npnn \__osgstyler_decoration_component_if:nn #1#2
  { T, F, TF }
  {
    \clist_if_in:NnTF \c__osgstyler_decoration_families_clist {#2}
      {
        \prop_if_in:NnTF \g__osgstyler_decoration_prop
          { #1 / #2 / present }
          { \prg_return_true: } { \prg_return_false: }
      }
      {
        \msg_error:nnn { osgstyler } { unknown-decoration-family } {#2}
        \prg_return_false:
      }
  }

\NewDocumentCommand \OsgDecorationIfHasComponentTF { m m m m }
  { \__osgstyler_decoration_component_if:nnTF {#1}{#2}{#3}{#4} }

\NewDocumentCommand \OsgDecorationIfHasInstanceTF { m m m m m }
  {
    \prop_if_in:NnTF \g__osgstyler_decoration_prop
      { #1 / #2 / #3 / present } {#4}{#5}
  }

\NewExpandableDocumentCommand \OsgDecorationFamilies { }
  { \clist_use:Nn \c__osgstyler_decoration_families_clist { , } }

% Inline styles are the first composition layer.  The template owns visual
% resource selection and passes semantic and decorative work to replaceable
% processors.  Their default implementations are transparent.
\tl_new:N \l__osgstyler_inline_font_tl
\tl_new:N \l__osgstyler_inline_color_tl
\tl_new:N \l__osgstyler_inline_decoration_tl
\tl_new:N \l__osgstyler_inline_semantics_tl
\tl_new:N \l__osgstyler_inline_weight_tl
\tl_new:N \l__osgstyler_inline_shape_tl
\tl_new:N \l__osgstyler_inline_command_socket_tl
\prop_new:N \g__osgstyler_inline_command_socket_prop
\prop_new:N \g__osgstyler_inline_command_plug_prop

\msg_new:nnnn { osgstyler } { unknown-decoration-for-style }
  { Inline~style~uses~unknown~decoration~'#1'. }
  { Declare~the~decoration~before~using~the~inline~style. }
\msg_new:nnnn { osgstyler } { unknown-inline-weight }
  { Unknown~inline~font~weight~'#1'. }
  { Use~inherit,~regular,~or~bold. }
\msg_new:nnnn { osgstyler } { unknown-inline-shape }
  { Unknown~inline~font~shape~'#1'. }
  { Use~inherit,~upright,~italic,~slanted,~or~small-caps. }
\msg_new:nnnn { osgstyler } { unbound-inline-command }
  { Command~'#1'~is~not~managed~by~osgstyler. }
  { Bind~it~with~\token_to_str:N\BindOsgInlineCommand~first. }

% A theme author needs to know whether decorations actually render (so it
% can offer or withhold a highlight-based visual variant); an end user
% never sees a processor and shouldn't need to. That split is why this
% conditional exists next to a plain error message rather than one
% mechanism serving both audiences.
\bool_new:N \g__osgstyler_decoration_backend_bool
\NewDocumentCommand \OsgDecorationProcessorAvailableTF { m m }
  { \bool_if:NTF \g__osgstyler_decoration_backend_bool {#1}{#2} }

\msg_new:nnnn { osgstyler } { decoration-requires-lua }
  { Feature~'#1'~requires~LuaLaTeX. }
  { Compile~with~LuaLaTeX,~or~use~osgstyler~without~this~feature. }
\msg_new:nnnn { osgstyler } { unresolved-symbol }
  { Affix~symbol~'#1'~has~no~symbol~backend. }
  { Use~literal~content~until~a~symbol~set~is~active. }
\msg_new:nnnn { osgstyler } { unsupported-radius }
  { Inline~enclosure~radius~'#1'~is~not~supported. }
  { The~active~decoration~backend~uses~square~corners. }

% Line and background decorations only render safely across a line break
% via engine-level (LuaTeX) node processing; the default processor below
% therefore renders enclosure and affix material -- which need no such
% support -- and raises a plain, engine-focused error for the rest instead
% of silently producing nothing. A real backend (loaded separately, e.g.
% osgstyler-lua) replaces this default via \cs{SetOsgDecorationProcessor}
% before either family is ever requested.
\tl_new:N \l__osgstyler_decoration_content_tl
\tl_new:N \l__osgstyler_decoration_before_tl
\tl_new:N \l__osgstyler_decoration_after_tl
\tl_new:N \l__osgstyler_decoration_value_tl
\tl_new:N \l__osgstyler_decoration_gap_tl
\tl_new:N \l__osgstyler_decoration_color_tl
\tl_new:N \l__osgstyler_decoration_instances_tl
\seq_new:N \l__osgstyler_decoration_metric_seq

\cs_new_protected:Npn \__osgstyler_decoration_value:nnnnN #1#2#3#4#5
  { \tl_set:Nx #5 { \OsgDecorationInstanceValue {#1}{#2}{#3}{#4} } }

\cs_new_protected:Npn \__osgstyler_decoration_default:Nn #1#2
  { \tl_if_empty:NT #1 { \tl_set:Nn #1 {#2} } }

\cs_new_protected:Npn \__osgstyler_decoration_metric_resolve:nN #1#2
  {
    \seq_set_split:Nnn \l__osgstyler_decoration_metric_seq {/}{#1}
    \int_compare:nNnTF { \seq_count:N \l__osgstyler_decoration_metric_seq } = { 2 }
      {
        \tl_set:Nx #2
          {
            \OsgMetric
              { \seq_item:Nn \l__osgstyler_decoration_metric_seq {1} }
              { \seq_item:Nn \l__osgstyler_decoration_metric_seq {2} }
          }
      }
      { \tl_set:Nn #2 {#1} }
  }

\cs_new_protected:Npn \__osgstyler_decoration_affix_material:nnn #1#2#3
  {
    \__osgstyler_decoration_value:nnnnN {#1}{affix}{#2}{gap}
      \l__osgstyler_decoration_gap_tl
    \__osgstyler_decoration_default:Nn \l__osgstyler_decoration_gap_tl { 0pt }
    \exp_args:NV \__osgstyler_decoration_metric_resolve:nN
      \l__osgstyler_decoration_gap_tl \l__osgstyler_decoration_gap_tl
    \str_if_eq:nnT {#3}{after} { \hspace{\l__osgstyler_decoration_gap_tl} }
    \group_begin:
      \__osgstyler_decoration_value:nnnnN {#1}{affix}{#2}{font}
        \l__osgstyler_decoration_value_tl
      \tl_if_empty:NF \l__osgstyler_decoration_value_tl
        { \exp_args:NV \UseOsgFont \l__osgstyler_decoration_value_tl }
      \__osgstyler_decoration_value:nnnnN {#1}{affix}{#2}{color}
        \l__osgstyler_decoration_color_tl
      \tl_if_empty:NF \l__osgstyler_decoration_color_tl
        {
          \tl_set:Nx \l__osgstyler_decoration_color_tl
            { \OsgColorName { \l__osgstyler_decoration_color_tl } }
          \color{\l__osgstyler_decoration_color_tl}
        }
      \__osgstyler_decoration_value:nnnnN {#1}{affix}{#2}{content}
        \l__osgstyler_decoration_value_tl
      \tl_if_empty:NTF \l__osgstyler_decoration_value_tl
        {
          \__osgstyler_decoration_value:nnnnN {#1}{affix}{#2}{symbol}
            \l__osgstyler_decoration_value_tl
          \tl_if_empty:NF \l__osgstyler_decoration_value_tl
            { \msg_warning:nne { osgstyler } { unresolved-symbol }
                \l__osgstyler_decoration_value_tl }
        }
        { \tl_use:N \l__osgstyler_decoration_value_tl }
    \group_end:
    \str_if_eq:nnT {#3}{before} { \hspace{\l__osgstyler_decoration_gap_tl} }
  }

\cs_new_protected:Npn \__osgstyler_decoration_affixes_apply:nn #1#2
  {
    \tl_clear:N \l__osgstyler_decoration_before_tl
    \tl_clear:N \l__osgstyler_decoration_after_tl
    \tl_set:Nx \l__osgstyler_decoration_instances_tl
      { \OsgDecorationInstances {#1}{affix} }
    \clist_map_inline:Vn \l__osgstyler_decoration_instances_tl
      {
        \__osgstyler_decoration_value:nnnnN {#1}{affix}{##1}{position}
          \l__osgstyler_decoration_value_tl
        \str_if_eq:VnTF \l__osgstyler_decoration_value_tl { after }
          {
            \tl_put_right:Nn \l__osgstyler_decoration_after_tl
              { \__osgstyler_decoration_affix_material:nnn {#1}{##1}{after} }
          }
          {
            \tl_put_right:Nn \l__osgstyler_decoration_before_tl
              { \__osgstyler_decoration_affix_material:nnn {#1}{##1}{before} }
          }
      }
    \tl_set:Nx \l__osgstyler_decoration_content_tl
      {
        \exp_not:V \l__osgstyler_decoration_before_tl
        \exp_not:n {#2}
        \exp_not:V \l__osgstyler_decoration_after_tl
      }
  }

\cs_new_protected:Npn \__osgstyler_decoration_enclosure_apply:nnn #1#2#3
  {
    \group_begin:
      \__osgstyler_decoration_value:nnnnN {#1}{enclosure}{#2}{border-color}
        \l__osgstyler_decoration_color_tl
      \__osgstyler_decoration_default:Nn \l__osgstyler_decoration_color_tl { primary }
      \tl_set:Nx \l__osgstyler_decoration_color_tl
        { \OsgColorName { \l__osgstyler_decoration_color_tl } }
      \__osgstyler_decoration_value:nnnnN {#1}{enclosure}{#2}{fill-color}
        \l__osgstyler_decoration_value_tl
      \__osgstyler_decoration_default:Nn \l__osgstyler_decoration_value_tl { background }
      \tl_set:Nx \l_tmpa_tl { \OsgColorName { \l__osgstyler_decoration_value_tl } }
      \__osgstyler_decoration_value:nnnnN {#1}{enclosure}{#2}{thickness}
        \l__osgstyler_decoration_value_tl
      \__osgstyler_decoration_default:Nn \l__osgstyler_decoration_value_tl { rule/thin }
      \exp_args:NV \__osgstyler_decoration_metric_resolve:nN
        \l__osgstyler_decoration_value_tl \l__osgstyler_decoration_value_tl
      \setlength\fboxrule{\l__osgstyler_decoration_value_tl}
      \__osgstyler_decoration_value:nnnnN {#1}{enclosure}{#2}{padding}
        \l__osgstyler_decoration_value_tl
      \__osgstyler_decoration_default:Nn \l__osgstyler_decoration_value_tl { padding/normal }
      \exp_args:NV \__osgstyler_decoration_metric_resolve:nN
        \l__osgstyler_decoration_value_tl \l__osgstyler_decoration_value_tl
      \setlength\fboxsep{\l__osgstyler_decoration_value_tl}
      \__osgstyler_decoration_value:nnnnN {#1}{enclosure}{#2}{radius}
        \l__osgstyler_decoration_value_tl
      \tl_if_empty:NF \l__osgstyler_decoration_value_tl
        {
          \str_if_eq:VnF \l__osgstyler_decoration_value_tl { 0pt }
            { \msg_warning:nne { osgstyler } { unsupported-radius }
                \l__osgstyler_decoration_value_tl }
        }
      \fcolorbox{\l__osgstyler_decoration_color_tl}{\l_tmpa_tl}{#3}
    \group_end:
  }

\cs_new_protected:Npn \__osgstyler_decoration_processor_default:nn #1#2
  {
    \group_begin:
      \OsgDecorationIfHasComponentTF {#1}{background}
        { \msg_error:nnn { osgstyler } { decoration-requires-lua } {#1} }
        { }
      \OsgDecorationIfHasComponentTF {#1}{line}
        { \msg_error:nnn { osgstyler } { decoration-requires-lua } {#1} }
        { }
      \__osgstyler_decoration_affixes_apply:nn {#1}{#2}
      \OsgDecorationIfHasComponentTF {#1}{enclosure}
        {
          \tl_set:Nx \l__osgstyler_decoration_instances_tl
            { \OsgDecorationInstances {#1}{enclosure} }
          \clist_map_inline:Vn \l__osgstyler_decoration_instances_tl
            {
              \tl_set:Nx \l__osgstyler_decoration_content_tl
                {
                  \__osgstyler_decoration_enclosure_apply:nnn {#1}{##1}
                    { \exp_not:V \l__osgstyler_decoration_content_tl }
                }
            }
        }
        { }
      \tl_use:N \l__osgstyler_decoration_content_tl
    \group_end:
  }
\cs_new_protected:Npn \__osgstyler_semantic_processor_default:nn #1#2 {#2}

% Decoration and semantic processors are each stored as a sequence,
% not a single macro, so that a later \AddOsg...Processor can extend
% \SetOsg...Processor (which resets the sequence to one entry, so
% today's replace-the-single-processor behavior is unchanged) without
% breaking the storage format. Applying the sequence folds it left to
% right: each processor wraps the previous one's output, so the
% first-added processor sees the original content and the
% last-added one sees the result of all earlier processors.
\seq_new:N \g__osgstyler_decoration_processor_seq
\seq_gput_right:Nn \g__osgstyler_decoration_processor_seq
  { \__osgstyler_decoration_processor_default:nn }
\seq_new:N \g__osgstyler_semantic_processor_seq
\seq_gput_right:Nn \g__osgstyler_semantic_processor_seq
  { \__osgstyler_semantic_processor_default:nn }

\tl_new:N \l__osgstyler_processor_content_tl
\cs_new_protected:Npn \__osgstyler_processor_chain_apply:Nnn #1#2#3
  {
    \group_begin:
      \tl_set:Nn \l__osgstyler_processor_content_tl {#3}
      \seq_map_inline:Nn #1
        {
          \tl_set:Nx \l__osgstyler_processor_content_tl
            { ##1 {#2} { \exp_not:o {\l__osgstyler_processor_content_tl} } }
        }
      \tl_use:N \l__osgstyler_processor_content_tl
    \group_end:
  }
\cs_new_protected:Npn \__osgstyler_decoration_processor:nn #1#2
  {
    \__osgstyler_processor_chain_apply:Nnn
      \g__osgstyler_decoration_processor_seq {#1}{#2}
  }
\cs_new_protected:Npn \__osgstyler_semantic_processor:nn #1#2
  {
    \__osgstyler_processor_chain_apply:Nnn
      \g__osgstyler_semantic_processor_seq {#1}{#2}
  }

\NewDocumentCommand \SetOsgDecorationProcessor { m }
  {
    \seq_gclear:N \g__osgstyler_decoration_processor_seq
    \seq_gput_right:Nn \g__osgstyler_decoration_processor_seq {#1}
  }
\NewDocumentCommand \SetOsgSemanticProcessor { m }
  {
    \seq_gclear:N \g__osgstyler_semantic_processor_seq
    \seq_gput_right:Nn \g__osgstyler_semantic_processor_seq {#1}
  }

\cs_new_protected:Npn \__osgstyler_inline_visual_apply:n #1
  {
    \group_begin:
      \tl_if_eq:VnF \l__osgstyler_inline_font_tl { none }
        { \exp_args:NV \UseOsgFont \l__osgstyler_inline_font_tl }
      \str_case:VnF \l__osgstyler_inline_weight_tl
        {
          { inherit } { }
          { regular } { \mdseries }
          { bold }    { \bfseries }
        }
        { \msg_error:nne { osgstyler } { unknown-inline-weight }
            { \l__osgstyler_inline_weight_tl } }
      \str_case:VnF \l__osgstyler_inline_shape_tl
        {
          { inherit }    { }
          { upright }    { \upshape }
          { italic }     { \itshape }
          { slanted }    { \slshape }
          { small-caps } { \scshape }
        }
        { \msg_error:nne { osgstyler } { unknown-inline-shape }
            { \l__osgstyler_inline_shape_tl } }
      \tl_if_eq:VnF \l__osgstyler_inline_color_tl { none }
        {
          \exp_args:NNV \clist_if_in:NnTF
            \c__osgstyler_color_slots_clist \l__osgstyler_inline_color_tl
            {
              \use:e
                {
                  \exp_not:N \color
                    { \OsgColorName { \l__osgstyler_inline_color_tl } }
                }
            }
            { \msg_error:nne { osgstyler } { unknown-color-slot }
                \l__osgstyler_inline_color_tl }
        }
      \tl_if_eq:VnTF \l__osgstyler_inline_decoration_tl { none }
        {#1}
        {
          \prop_if_in:NeTF \g__osgstyler_decoration_prop
            { \l__osgstyler_inline_decoration_tl / declared }
            {
              \exp_args:NV \__osgstyler_decoration_processor:nn
                \l__osgstyler_inline_decoration_tl {#1}
            }
            {
              \msg_error:nne { osgstyler } { unknown-decoration-for-style }
                \l__osgstyler_inline_decoration_tl
              #1
            }
        }
    \group_end:
  }

\cs_new_protected:Npn \__osgstyler_inline_apply:n #1
  {
    \tl_if_eq:VnTF \l__osgstyler_inline_semantics_tl { none }
      { \__osgstyler_inline_visual_apply:n {#1} }
      {
        \exp_args:NV \__osgstyler_semantic_processor:nn
          \l__osgstyler_inline_semantics_tl
          { \__osgstyler_inline_visual_apply:n {#1} }
      }
  }

\NewTemplateType { osgstyler-inline } { 1 }
\DeclareTemplateInterface { osgstyler-inline } { standard } { 1 }
  {
    font       : tokenlist = main,
    weight     : tokenlist = inherit,
    shape      : tokenlist = inherit,
    color      : tokenlist = text,
    decoration : tokenlist = none,
    semantics  : tokenlist = none
  }
\DeclareTemplateCode { osgstyler-inline } { standard } { 1 }
  {
    font       = \l__osgstyler_inline_font_tl,
    weight     = \l__osgstyler_inline_weight_tl,
    shape      = \l__osgstyler_inline_shape_tl,
    color      = \l__osgstyler_inline_color_tl,
    decoration = \l__osgstyler_inline_decoration_tl,
    semantics  = \l__osgstyler_inline_semantics_tl
  }
  { \__osgstyler_inline_apply:n {#1} }

\NewDocumentCommand \DeclareOsgInlineStyle { m m }
  { \DeclareInstance { osgstyler-inline } {#1} { standard } {#2} }

\NewDocumentCommand \UseOsgInlineStyle { m m }
  { \UseInstance { osgstyler-inline } {#1} {#2} }

\NewDocumentCommand \EditOsgInlineStyle { m m }
  { \EditInstance { osgstyler-inline } {#1} {#2} }

\prop_new:N \g__osgstyler_mode_aware_command_prop

\cs_new_protected:Npn \__osgstyler_make_mode_aware:Nn #1#2
  {
    \cs_if_exist:NT \MakeOverlayAwareCommand
      {
        \prop_if_in:NeF \g__osgstyler_mode_aware_command_prop
          { \cs_to_str:N #1 }
          {
            \MakeOverlayAwareCommand
              [ arguments = {#2}, fallback = flatten ] #1
            \prop_gput:Nee \g__osgstyler_mode_aware_command_prop
              { \cs_to_str:N #1 } { true }
          }
      }
  }

\cs_new_protected:Npn \__osgstyler_inline_command_define_existing:Nn #1#2
  { \RenewDocumentCommand #1 { +m } { \UseSocket {#2} {##1} } }

\cs_new_protected:Npn \__osgstyler_inline_command_define_new:Nn #1#2
  { \NewDocumentCommand #1 { +m } { \UseSocket {#2} {##1} } }

\cs_new_protected:Npn \__osgstyler_inline_command_socket_new:N #1
  {
    \tl_set:Nx \l__osgstyler_inline_command_socket_tl
      { osgstyler/inline-command/\cs_to_str:N #1 }
    \NewSocket { \l__osgstyler_inline_command_socket_tl } { 1 }
    \cs_if_exist:NTF #1
      {
        \exp_args:Nc \NewCommandCopy
          { __osgstyler_inline_original_\cs_to_str:N #1:n } #1
        \NewSocketPlug { \l__osgstyler_inline_command_socket_tl } { original }
          {
            \use:c
              { __osgstyler_inline_original_\cs_to_str:N #1:n } {##1}
          }
        \NewSocketPlug { \l__osgstyler_inline_command_socket_tl } { default }
          {
            \use:c
              { __osgstyler_inline_original_\cs_to_str:N #1:n } {##1}
          }
        \prop_gput:Nee \g__osgstyler_inline_command_plug_prop
          { \l__osgstyler_inline_command_socket_tl / original } { true }
        \exp_args:NNV \__osgstyler_inline_command_define_existing:Nn
          #1 \l__osgstyler_inline_command_socket_tl
      }
      {
        \NewSocketPlug { \l__osgstyler_inline_command_socket_tl } { default }
          {##1}
        \exp_args:NNV \__osgstyler_inline_command_define_new:Nn
          #1 \l__osgstyler_inline_command_socket_tl
      }
    \prop_gput:Nee \g__osgstyler_inline_command_socket_prop
      { \cs_to_str:N #1 } { \l__osgstyler_inline_command_socket_tl }
  }

\cs_new_protected:Npn \__osgstyler_inline_command_bind:Nn #1#2
  {
    \prop_get:NeNTF \g__osgstyler_inline_command_socket_prop
      { \cs_to_str:N #1 } \l__osgstyler_inline_command_socket_tl
      { }
      { \__osgstyler_inline_command_socket_new:N #1 }
    \prop_if_in:NeF \g__osgstyler_inline_command_plug_prop
      { \l__osgstyler_inline_command_socket_tl / #2 }
      {
        \NewSocketPlug { \l__osgstyler_inline_command_socket_tl }
          { style/#2 } { \UseOsgInlineStyle {#2} {##1} }
        \prop_gput:Nee \g__osgstyler_inline_command_plug_prop
          { \l__osgstyler_inline_command_socket_tl / #2 } { true }
      }
    \AssignSocketPlug { \l__osgstyler_inline_command_socket_tl } { style/#2 }
    \__osgstyler_make_mode_aware:Nn #1 { +m }
    % Bound inline commands have exactly one content argument and are therefore
    % transparent to presitemize's initial-case transformation.  The package
    % hook also covers the load order osgstyler -> presitemize.
    \cs_if_exist:NTF \DeclarePresInitialTransparentCommand
      { \DeclarePresInitialTransparentCommand #1 }
      {
        \AddToHook { package/osglecture-presitemize/after }
          { \DeclarePresInitialTransparentCommand #1 }
      }
  }

% The binding assumes a single, long (+m) argument; this covers the
% common case (\emph, \textbf, and similar) but is not a general
% command-signature adapter. A future `content=<n>` selector is
% reserved for commands with more than one argument, styling argument
% n and passing the rest through unchanged (e.g. \href{url}{text}).
% `content=0` is reserved within that scheme for styling a command's
% fully expanded result rather than one of its arguments (e.g.
% \ref{label}), which needs box capture or expansion capture instead
% of argument wrapping and so is a separate extension, not a variant
% of the content=n case.
\NewDocumentCommand \BindOsgInlineCommand { m m }
  { \__osgstyler_inline_command_bind:Nn #1 {#2} }

\NewDocumentCommand \RestoreOsgInlineCommand { m }
  {
    \prop_get:NeNTF \g__osgstyler_inline_command_socket_prop
      { \cs_to_str:N #1 } \l__osgstyler_inline_command_socket_tl
      {
        \AssignSocketPlug { \l__osgstyler_inline_command_socket_tl }
          { default }
      }
      { \msg_error:nne { osgstyler } { unbound-inline-command }
          { \token_to_str:N #1 } }
  }

\cs_new_protected:Npn \__osgstyler_inline_command_default:N #1
  {
    \prop_get:NeNTF \g__osgstyler_inline_command_socket_prop
      { \cs_to_str:N #1 } \l__osgstyler_inline_command_socket_tl
      { }
      { \__osgstyler_inline_command_socket_new:N #1 }
    \AssignSocketPlug { \l__osgstyler_inline_command_socket_tl } { default }
  }

\DeclareOsgDecoration { osgstyler-underline }
  {
    line =
      {
        default =
          { position=below, pattern=solid, color=text,
            thickness=rule/thin, offset=.1ex }
      }
  }
\DeclareOsgDecoration { osgstyler-highlight }
  { background = { color=accent, opacity=1, shape=text } }
\DeclareOsgDecoration { osgstyler-frame }
  {
    enclosure =
      { border-color=primary, fill-color=background, pattern=solid,
        thickness=rule/thin, radius=0pt, padding=padding/normal }
  }

\DeclareOsgInlineStyle { emphasis }
  { font=none, weight=inherit, shape=italic, color=none,
    decoration=none, semantics=emphasis }
\DeclareOsgInlineStyle { strong }
  { font=none, weight=bold, shape=inherit, color=none,
    decoration=none, semantics=strong }
\DeclareOsgInlineStyle { underline }
  { font=none, color=none, decoration=osgstyler-underline, semantics=none }
\DeclareOsgInlineStyle { highlight }
  { font=none, color=none, decoration=osgstyler-highlight, semantics=none }
\DeclareOsgInlineStyle { frame }
  { font=none, color=none, decoration=osgstyler-frame, semantics=none }

\NewDocumentCommand \UseOsgStandardInlineCommands { }
  {
    \BindOsgInlineCommand \emph { emphasis }
    \BindOsgInlineCommand \textbf { strong }
    \BindOsgInlineCommand \uline { underline }
    \BindOsgInlineCommand \highlight { highlight }
    \BindOsgInlineCommand \fbox { frame }
  }

\NewDocumentCommand \UseOsgDefaultInlineCommands { }
  {
    \__osgstyler_inline_command_default:N \emph
    \__osgstyler_inline_command_default:N \textbf
    \__osgstyler_inline_command_default:N \uline
    \__osgstyler_inline_command_default:N \highlight
    \__osgstyler_inline_command_default:N \fbox
  }

\BindOsgInlineCommand \highlight { highlight }

% ==========================================
% BOX TEMPLATE: STYLED BLOCKS
% ==========================================
%
% \ldeen*{Ein gemeinsamer Template-Typ für kastenartige, umrandete oder
% hinterlegte Inhalte -- Beamer-Blöcke ebenso wie theorem-artige Boxen
% oder Zitate in der Langform. Paket- und Themenautoren binden entweder
% eine bereits bekannte Umgebung an eine Instanz (die Render-Logik
% umhüllt dann nur optisch, die Umgebung bringt z.\,B. ihre eigene
% Nummerierung selbst mit) oder deklarieren über
% \cs{NewOsgStyledEnvironment} eine neue -- die Render-Logik existiert
% in beiden Fällen nur einmal. Farbwerte werden stets über
% \cs{OsgColorName} aufgelöst statt als bloße Slot-Namen verwendet, da
% nur die aufgelösten Namen unabhängig von einer ladenden Theme-Datei
% wie \pkg*{lttheme} garantiert existieren.}
% {A shared template type for boxed, framed or filled content -- beamer
% blocks as well as theorem-like boxes or quotations in the long form.
% Package and theme authors either bind an already-known environment to
% an instance (the rendering then only wraps it visually; the
% environment brings its own numbering, if any) or declare a new one via
% \cs{NewOsgStyledEnvironment} -- the rendering logic exists only once
% either way. Color values are always resolved via \cs{OsgColorName}
% rather than used as bare slot names, since only the resolved names are
% guaranteed to exist independently of a loading theme file such as
% \pkg*{lttheme}.}

\tl_new:N \l__osgstyler_block_background_tl
\tl_new:N \l__osgstyler_block_rule_color_tl
\tl_new:N \l__osgstyler_block_before_tl
\tl_new:N \l__osgstyler_block_after_tl
\tl_new:N \l__osgstyler_block_layout_tl
\tl_new:N \l__osgstyler_block_head_bg_tl
\tl_new:N \l__osgstyler_block_head_fg_tl
\tl_new:N \l__osgstyler_block_head_font_tl
\tl_new:N \l__osgstyler_block_head_rule_color_tl
\tl_new:N \l__osgstyler_block_foot_rule_color_tl
\tl_new:N \l__osgstyler_block_body_bg_tl
\dim_new:N \l__osgstyler_block_rule_dim
\dim_new:N \l__osgstyler_block_padding_dim
\dim_new:N \l__osgstyler_block_padding_top_dim
\dim_new:N \l__osgstyler_block_padding_right_dim
\dim_new:N \l__osgstyler_block_padding_bottom_dim
\dim_new:N \l__osgstyler_block_padding_left_dim
\tl_new:N \l__osgstyler_block_shadow_color_tl
\dim_new:N \l__osgstyler_block_corner_radius_dim
\dim_new:N \l__osgstyler_block_shadow_offset_dim
\dim_new:N \l__osgstyler_block_head_padding_dim
\dim_new:N \l__osgstyler_block_head_padding_top_dim
\dim_new:N \l__osgstyler_block_head_padding_right_dim
\dim_new:N \l__osgstyler_block_head_padding_bottom_dim
\dim_new:N \l__osgstyler_block_head_padding_left_dim
\dim_new:N \l__osgstyler_block_head_rule_dim
\dim_new:N \l__osgstyler_block_foot_rule_dim
\dim_new:N \l__osgstyler_block_body_padding_dim
\dim_new:N \l__osgstyler_block_body_padding_top_dim
\dim_new:N \l__osgstyler_block_body_padding_right_dim
\dim_new:N \l__osgstyler_block_body_padding_bottom_dim
\dim_new:N \l__osgstyler_block_body_padding_left_dim
\bool_new:N \l__osgstyler_block_shadow_bool

% \ldeen*{Der Titel wird nicht als Template-Schlüssel geführt (er wechselt
% je Verwendung, nicht je Instanz), sondern vor \cs{UseInstance} in diese
% Variable gelegt -- über \cs{__osgstyler_block_use:nnn}, damit keine
% Aufrufstelle sie zu setzen vergisst. Im Layout \code{plain} wird er
% schlicht dem Body vorangestellt (verhält sich wie das frühere
% \code{Titel\cs{par}Body}); im Layout \code{titled} bekommt er einen
% eigenen, gestalteten Kopf.}
% {The title is not a template key (it changes per use, not per instance);
% callers place it into this variable before \cs{UseInstance}, through
% \cs{__osgstyler_block_use:nnn} so no call site forgets it. In layout
% \code{plain} it is simply prepended to the body (behaving like the
% former \code{title\cs{par}body}); in layout \code{titled} it gets its
% own styled head.}
\tl_new:N \l__osgstyler_block_title_tl

\NewTemplateType { osgstyler-block } { 1 }
\DeclareTemplateInterface { osgstyler-block } { standard } { 1 }
  {
    background    : tokenlist = none,
    rule-color    : tokenlist = structure,
    rule-thickness: length = 0pt,
    padding       : length = 0pt,
    padding-top   : length = -1pt,
    padding-right : length = -1pt,
    padding-bottom: length = -1pt,
    padding-left  : length = -1pt,
    corner-radius : length = 0pt,
    shadow        : boolean = false,
    shadow-offset : length = 2pt,
    shadow-color  : tokenlist = structure!30,
    before        : tokenlist =,
    after         : tokenlist =,
    layout        : choice { plain, titled } = plain,
    head-background     : tokenlist = structure,
    head-color         : tokenlist = background,
    head-font          : tokenlist = \normalfont \bfseries,
    head-padding       : length = 0.35em,
    head-padding-top   : length = -1pt,
    head-padding-right : length = -1pt,
    head-padding-bottom: length = -1pt,
    head-padding-left  : length = -1pt,
    head-rule-color    : tokenlist = structure,
    head-rule-thickness: length = 0pt,
    foot-rule-color    : tokenlist = structure,
    foot-rule-thickness: length = 0pt,
    body-background    : tokenlist = none,
    body-padding       : length = 0.4em,
    body-padding-top   : length = -1pt,
    body-padding-right : length = -1pt,
    body-padding-bottom: length = -1pt,
    body-padding-left  : length = -1pt
  }
\DeclareTemplateCode { osgstyler-block } { standard } { 1 }
  {
    background = \l__osgstyler_block_background_tl,
    rule-color = \l__osgstyler_block_rule_color_tl,
    rule-thickness = \l__osgstyler_block_rule_dim,
    padding = \l__osgstyler_block_padding_dim,
    padding-top = \l__osgstyler_block_padding_top_dim,
    padding-right = \l__osgstyler_block_padding_right_dim,
    padding-bottom = \l__osgstyler_block_padding_bottom_dim,
    padding-left = \l__osgstyler_block_padding_left_dim,
    corner-radius = \l__osgstyler_block_corner_radius_dim,
    shadow = \l__osgstyler_block_shadow_bool,
    shadow-offset = \l__osgstyler_block_shadow_offset_dim,
    shadow-color = \l__osgstyler_block_shadow_color_tl,
    before = \l__osgstyler_block_before_tl,
    after = \l__osgstyler_block_after_tl,
    layout =
      {
        plain  = \tl_set:Nn \l__osgstyler_block_layout_tl { plain },
        titled = \tl_set:Nn \l__osgstyler_block_layout_tl { titled }
      },
    head-background = \l__osgstyler_block_head_bg_tl,
    head-color = \l__osgstyler_block_head_fg_tl,
    head-font = \l__osgstyler_block_head_font_tl,
    head-padding = \l__osgstyler_block_head_padding_dim,
    head-padding-top = \l__osgstyler_block_head_padding_top_dim,
    head-padding-right = \l__osgstyler_block_head_padding_right_dim,
    head-padding-bottom = \l__osgstyler_block_head_padding_bottom_dim,
    head-padding-left = \l__osgstyler_block_head_padding_left_dim,
    head-rule-color = \l__osgstyler_block_head_rule_color_tl,
    head-rule-thickness = \l__osgstyler_block_head_rule_dim,
    foot-rule-color = \l__osgstyler_block_foot_rule_color_tl,
    foot-rule-thickness = \l__osgstyler_block_foot_rule_dim,
    body-background = \l__osgstyler_block_body_bg_tl,
    body-padding = \l__osgstyler_block_body_padding_dim,
    body-padding-top = \l__osgstyler_block_body_padding_top_dim,
    body-padding-right = \l__osgstyler_block_body_padding_right_dim,
    body-padding-bottom = \l__osgstyler_block_body_padding_bottom_dim,
    body-padding-left = \l__osgstyler_block_body_padding_left_dim
  }
  { \__osgstyler_block_padding_resolve: \__osgstyler_block_typeset:n {#1} }

\cs_new_protected:Npn \__osgstyler_block_padding_fallback:NN #1#2
  { \dim_compare:nNnT {#1} < { 0pt } { \dim_set_eq:NN #1#2 } }
\cs_new_protected:Npn \__osgstyler_block_padding_resolve:
  {
    \clist_map_inline:nn { top,right,bottom,left }
      {
        \exp_args:Nc \__osgstyler_block_padding_fallback:NN
          { l__osgstyler_block_padding_##1_dim }
          \l__osgstyler_block_padding_dim
        \exp_args:Nc \__osgstyler_block_padding_fallback:NN
          { l__osgstyler_block_head_padding_##1_dim }
          \l__osgstyler_block_head_padding_dim
        \exp_args:Nc \__osgstyler_block_padding_fallback:NN
          { l__osgstyler_block_body_padding_##1_dim }
          \l__osgstyler_block_body_padding_dim
      }
  }

% \ldeen*{Einzige Aufrufkonvention für den Block-Template-Typ: Titel und
% Body getrennt übergeben. Aufrufstellen ohne Titel geben \code{\{\}}.}
% {The one calling convention for the block template type: pass title and
% body separately. Call sites without a title pass \code{\{\}}.}
\cs_new_protected:Npn \__osgstyler_block_use:nnn #1#2#3
  {
    \tl_set:Nn \l__osgstyler_block_title_tl {#2}
    \UseInstance { osgstyler-block } {#1} {#3}
  }

% \ldeen*{Muss expandierbar sein: der Wert landet als Farbargument in
% \cs{colorbox}/\cs{fcolorbox}, die ihr Argument expandieren.}
% {Must stay expandable: the value ends up as the colour argument of
% \cs{colorbox}/\cs{fcolorbox}, which expand their argument.}
\cs_new:Npn \__osgstyler_block_bg:n #1
  { \str_if_eq:nnTF {#1} { none } { \OsgColorName{background} } { \OsgColorName{#1} } }
\cs_generate_variant:Nn \__osgstyler_block_bg:n { V }

% \ldeen*{Kastenhintergrund + Rahmen. Ohne \code{corner-radius} und
% \code{shadow} genügt \cs{fcolorbox} (\cs{fboxsep} ist bei den Aufrufern
% Null). Sind Ecken oder Schatten gewünscht, zeichnet \pkg{pgfcore} die
% Form: der zu umrahmende Kasten wird vermessen, Schatten/Fläche/Rand
% laufen als \code{artifact}-\cs{rlap} dahinter, der Kasten selbst behält
% seine Struktur. \pkg{pgfcore} lädt das Theme (z.\,B.\ \pkg*{magpie});
% fehlt es, fällt der Renderer auf eckig ohne Schatten zurück.}
% {Box background + frame. Without \code{corner-radius} and \code{shadow}
% an \cs{fcolorbox} is enough (\cs{fboxsep} is zero at the call sites).
% When corners or a shadow are wanted, \pkg{pgfcore} draws the shape: the
% box to be framed is measured, shadow/fill/border run behind it as an
% \code{artifact} \cs{rlap}, and the box keeps its own structure. The theme
% loads \pkg{pgfcore} (e.g.\ \pkg*{magpie}); without it the renderer falls
% back to square with no shadow.}
\box_new:N \l__osgstyler_block_content_box
\msg_new:nnn { osgstyler } { no-pgfcore }
  { Rounded~corners~and~shadows~on~blocks~need~\token_to_str:N\usepackage
    {pgfcore}.~Falling~back~to~a~square~frame. }
\bool_new:N \g__osgstyler_block_pgf_warned_bool
\cs_new_protected:Npn \__osgstyler_block_fbox:nnn #1#2#3
  {
    \bool_lazy_and:nnTF
      { \cs_if_exist_p:N \pgfpicture }
      {
        \bool_lazy_or_p:nn
          { \dim_compare_p:nNn \l__osgstyler_block_corner_radius_dim > \c_zero_dim }
          { \bool_if_p:N \l__osgstyler_block_shadow_bool }
      }
      { \__osgstyler_block_fbox_pgf:nnn {#1} {#2} {#3} }
      {
        \bool_lazy_and:nnT
          { ! \cs_if_exist_p:N \pgfpicture }
          {
            \bool_lazy_or_p:nn
              { \dim_compare_p:nNn \l__osgstyler_block_corner_radius_dim > \c_zero_dim }
              { \bool_if_p:N \l__osgstyler_block_shadow_bool }
          }
          {
            \bool_if:NF \g__osgstyler_block_pgf_warned_bool
              {
                \bool_gset_true:N \g__osgstyler_block_pgf_warned_bool
                \msg_warning:nn { osgstyler } { no-pgfcore }
              }
          }
        \fcolorbox {#1} {#2} {#3}
      }
  }
% \ldeen{Ein abgerundetes (bzw.\ bei Radius Null eckiges) Rechteck von
%   \code{(#1,#2)} mit Größe \code{#3}$\times$\code{#4} als aktuellem
%   pgf-Pfad.}{A rounded (square at radius zero) rectangle from
%   \code{(#1,#2)} of size \code{#3}$\times$\code{#4} as the current pgf
%   path.}
\cs_new_protected:Npn \__osgstyler_block_rrect:nnnn #1#2#3#4
  {
    \pgfsetcornersarced
      { \pgfpoint
          { \l__osgstyler_block_corner_radius_dim }
          { \l__osgstyler_block_corner_radius_dim } }
    \pgfpathrectanglecorners
      { \pgfpoint { \dim_eval:n {#1} } { \dim_eval:n {#2} } }
      { \pgfpoint { \dim_eval:n { (#1) + (#3) } } { \dim_eval:n { (#2) + (#4) } } }
  }
\cs_new_protected:Npn \__osgstyler_block_fbox_pgf:nnn #1#2#3
  {
    \hbox_set:Nn \l__osgstyler_block_content_box {#3}
    \hbox:n
      {
        \tag_mc_begin:n { artifact }
          \rlap
            {
              \begin { pgfpicture }
                \pgfsetbaseline { 0pt }
                \bool_if:NT \l__osgstyler_block_shadow_bool
                  {
                    \pgfsetfillcolor
                      { \OsgColorName { \l__osgstyler_block_shadow_color_tl } }
                    \__osgstyler_block_rrect:nnnn
                      { \l__osgstyler_block_shadow_offset_dim }
                      { -\box_dp:N \l__osgstyler_block_content_box
                        - \l__osgstyler_block_shadow_offset_dim }
                      { \box_wd:N \l__osgstyler_block_content_box }
                      { \box_ht:N \l__osgstyler_block_content_box
                        + \box_dp:N \l__osgstyler_block_content_box }
                    \pgfusepath { fill }
                  }
                \pgfsetfillcolor { #2 }
                \pgfsetstrokecolor { #1 }
                \pgfsetlinewidth { \l__osgstyler_block_rule_dim }
                \__osgstyler_block_rrect:nnnn
                  { 0pt }
                  { -\box_dp:N \l__osgstyler_block_content_box }
                  { \box_wd:N \l__osgstyler_block_content_box }
                  { \box_ht:N \l__osgstyler_block_content_box
                    + \box_dp:N \l__osgstyler_block_content_box }
                \dim_compare:nNnTF \l__osgstyler_block_rule_dim > \c_zero_dim
                  { \pgfusepath { fill, stroke } }
                  { \pgfusepath { fill } }
              \end { pgfpicture }
            }%
        \tag_mc_end:
        \box_use_drop:N \l__osgstyler_block_content_box
      }
  }

\cs_new_protected:Npn \__osgstyler_block_typeset:n #1
  {
    \group_begin:
      \l__osgstyler_block_before_tl
      \str_if_eq:VnTF \l__osgstyler_block_layout_tl { titled }
        { \__osgstyler_block_typeset_titled:n {#1} }
        { \__osgstyler_block_typeset_plain:n {#1} }
      \l__osgstyler_block_after_tl
    \group_end:
  }

% \ldeen*{\code{plain}: Titel (falls gesetzt) plus Body, ungeteilt -- wie
% bisher, samt optionalem Rahmen um alles, wenn \code{rule-thickness}
% positiv ist.}
% {\code{plain}: title (if set) plus body, undivided -- as before,
% including an optional frame around everything when \code{rule-thickness}
% is positive.}
% \ldeen{\cs{tagpdfparaOff} um die \cs{fcolorbox}: ohne das umhüllt das
%   Absatz-Tagging der Klasse den Kasten in einem eigenen Absatz, den der
%   \env{minipage}-Sockel dann um sein \code{Div} herum aufspaltet und
%   leere \code{P}-Fragmente hinterlässt. Mit ausgeschaltetem Absatz-Tagging
%   liegt das \code{Div} sauber neben den umgebenden Absätzen. Wirkt nur bei
%   geladenem \pkg{tagpdf} und wird von der einschließenden Gruppe wieder
%   zurückgesetzt.}{\cs{tagpdfparaOff} around the \cs{fcolorbox}: without it
%   the class's paragraph tagging wraps the box in a paragraph of its own,
%   which the \env{minipage} socket then splits around its \code{Div},
%   leaving empty \code{P} fragments. With paragraph tagging off the
%   \code{Div} sits cleanly beside the surrounding paragraphs. Only matters
%   when \pkg{tagpdf} is loaded, and the enclosing group restores it.}
\cs_new_protected:Npn \__osgstyler_block_box_para_off:
  { \cs_if_exist_use:N \tagpdfparaOff }
% \ldeen{Setzt den Blocktitel im Kopf-Font und in der Kopffarbe (wie beim
%   \code{titled}-Layout), aber als gewöhnliche erste Absatzzeile.}{Typesets
%   the block title in the head font and head colour (as in the \code{titled}
%   layout), but as an ordinary first paragraph line.}
\cs_new_protected:Npn \__osgstyler_block_plain_title:
  {
    \tl_if_empty:NF \l__osgstyler_block_title_tl
      {
        { \color { \OsgColorName{ \l__osgstyler_block_head_fg_tl } }
          \l__osgstyler_block_head_font_tl
          \l__osgstyler_block_title_tl \par }
        \nobreak
        \skip_vertical:n { 0.3 \baselineskip }
      }
  }
\cs_new_protected:Npn \__osgstyler_block_typeset_plain:n #1
  {
    \dim_compare:nNnTF { \l__osgstyler_block_rule_dim } > { 0pt }
      {
        \par
        \group_begin:
          \__osgstyler_block_box_para_off:
          \addvspace { \medskipamount }
          \setlength\fboxsep{0pt}
          \setlength\fboxrule{\l__osgstyler_block_rule_dim}
          \noindent\fcolorbox
            { \OsgColorName{\l__osgstyler_block_rule_color_tl} }
            { \__osgstyler_block_bg:V \l__osgstyler_block_background_tl }
            {
              \begin{minipage}{\dim_eval:n
                {\linewidth-2\fboxrule}}
                \tex_kern:D \l__osgstyler_block_padding_top_dim
                \group_begin:
                  \dim_set:Nn \leftskip { \l__osgstyler_block_padding_left_dim }
                  \dim_set:Nn \rightskip { \l__osgstyler_block_padding_right_dim }
                  \__osgstyler_block_plain_title:
                  #1
                  \par
                \group_end:
                \tex_kern:D \l__osgstyler_block_padding_bottom_dim
              \end{minipage}
            }
          \par
        \group_end:
        \addvspace { \medskipamount }
      }
      {
        \__osgstyler_block_plain_title:
        #1
      }
  }

% \ldeen*{\code{titled}: farbiger Kopfstreifen mit dem Titel, darunter der
% eingerückte Body -- optisch abhebende Blöcke im Stil von Beamers Madrid.
%
% Zwei Varianten je nach \code{body-background}. Bei \code{none} fließt
% alles: der Titel als normale Textzeile mit dem farbigen Streifen als
% nullgroßer \cs{rlap}\cs{smash} dahinter, der Body mit
% \cs{leftskip}/\cs{rightskip} als Innenabstand. Titel und Body-Absätze
% sind dann Geschwister im Frame -- getaggt wie ein Absatz ohne Block, ohne
% zusätzliches \code{Div}. Eine getönte Body-Fläche braucht dagegen einen
% Kasten: ist \code{body-background} gesetzt, liegt der Block in genau
% einer \cs{fcolorbox} um eine \env{minipage}. \cs{tagpdfparaOff} um den
% Kasten hält das Absatz-Tagging der Klasse davon ab, ihn in einen eigenen,
% um das \code{Div} herum aufgespaltenen Absatz zu hüllen; das \code{Div}
% steht damit sauber neben den umgebenden Absätzen (wie bei \code{card}),
% mit \code{text-unit}s als Kindern, ohne leere Strukturelemente. Der
% Kopfbalken fließt textlos in die MC der Titelzeile. Die Kastenunterkante
% markiert das Blockende.}
% {\code{titled}: a coloured head strip carrying the title, the indented
% body below -- visually distinct blocks in the style of beamer's Madrid.
%
% Two variants depending on \code{body-background}. With \code{none}
% everything flows: the title as a normal text line with the coloured strip
% behind it as a zero-size \cs{rlap}\cs{smash}, the body with
% \cs{leftskip}/\cs{rightskip} as inner padding. Title and body paragraphs
% are then siblings in the frame -- tagged like a paragraph with no block,
% no extra \code{Div}. A tinted body area needs a box: when
% \code{body-background} is set the block sits in exactly one \cs{fcolorbox}
% around a \env{minipage}. \cs{tagpdfparaOff} around the box keeps the
% class's paragraph tagging from wrapping it in a paragraph of its own that
% would be split around the \code{Div}; the \code{Div} then sits cleanly
% beside the surrounding paragraphs (as with \code{card}), with
% \code{text-unit} children and no empty structure elements. The head bar
% flows textlessly into the title line's MC. The box's bottom edge marks
% where the block ends.}
% \ldeen{Eine volle Zeilenbreite überspannende Deko-Linie in eigener Zeile,
%   getaggt als ausgesetzt (nicht Teil der Struktur).}{A full-width
%   decorative rule on its own line, tagged as suspended (not part of the
%   structure).}
\cs_new_protected:Npn \__osgstyler_block_rule:nn #1#2
  {
    \tag_suspend:n { osgstyler-block-rule }
    \nobreak
    \noindent
    { \color { \OsgColorName{#1} } \rule { \linewidth } {#2} }%
    \par
    \tag_resume:n { osgstyler-block-rule }
  }
\cs_new_protected:Npn \__osgstyler_block_typeset_titled:n #1
  {
    \str_if_eq:VnTF \l__osgstyler_block_body_bg_tl { none }
      { \__osgstyler_block_typeset_titled_flow:n {#1} }
      { \__osgstyler_block_typeset_titled_box:n {#1} }
  }
% \ldeen{Kopfstreifen und Kopfbalken sind Dekoration und werden als
%   Artefakt markiert. Damit das die MC der Titelzeile nicht teilt und kein
%   leeres \code{P}-Fragment hinterlässt, steht das Artefakt \emph{ganz am
%   Absatzanfang}, unmittelbar nach \cs{noindent} und noch vor jedem
%   anderen Material der Zeile (auch vor dem unsichtbaren Strut des
%   Kastenzweigs, der deshalb mit hineingenommen wird): Der vom Absatz-Hook
%   geöffneten MC ist dann kein einziger Knoten zugeordnet, sie erscheint
%   im PDF gar nicht, und der Titeltext bildet nach dem Pop wieder genau
%   eine MC. Ohne offene MC (Absatz-Tagging aus) bleibt der Pop
%   wirkungslos; ohne geladenes tagpdf entfällt die Markierung ganz.}{Head strip and head bar are decoration and are marked as
%   artifact. So that this neither splits the title line's MC nor leaves
%   an empty \code{P} fragment, the artifact sits \emph{at the very start
%   of the paragraph}, right after \cs{noindent} and before any other
%   material of the line (including the boxed branch's invisible strut,
%   which is therefore taken inside): the MC opened by the paragraph hook
%   then has not a single node assigned, does not appear in the PDF at
%   all, and after the pop the title text again forms exactly one MC.
%   Without an open MC (paragraph tagging off) the pop has no effect;
%   without tagpdf loaded the marking is skipped altogether.}
\cs_new_protected:Npn \__osgstyler_block_head_artifact:n #1
  {
    \cs_if_exist:NTF \tag_mc_end_push:
      {
        \tag_mc_end_push:
        \tag_mc_begin:n { artifact }
        #1
        \tag_mc_end:
        \tag_mc_begin_pop:n { }
      }
      { #1 }
  }
% \ldeen{Der Kopfstreifen als nullgrosse \cs{rlap}\cs{smash}-Deko hinter der
%   Titelzeile; ohne Text trägt er nichts zur Vorlesereihenfolge bei. Aufruf
%   im hmode, innerhalb von \cs{__osgstyler_block_head_artifact:n}.
%   \code{#1} verschiebt die Streifenkante nach links, \code{#2} ist seine
%   Breite.}{The head strip as zero-size \cs{rlap}\cs{smash} decoration
%   behind the title line; carrying no text it adds nothing to the reading
%   order. Call in hmode, inside \cs{__osgstyler_block_head_artifact:n}.
%   \code{#1} shifts the strip edge left, \code{#2} its width.}
\cs_new_protected:Npn \__osgstyler_block_headstrip:nn #1#2
  {
    \str_if_eq:VnF \l__osgstyler_block_head_bg_tl { none }
      {
        \rlap { \smash {
          \skip_horizontal:n { #1 }
          \color { \OsgColorName{ \l__osgstyler_block_head_bg_tl } }
          \rule
            [ \dim_eval:n { -0.32 \baselineskip } ]
            { \dim_eval:n { #2 } }
            { \dim_eval:n { 1.2 \baselineskip } }
        } }%
      }
  }
% \ldeen{Der Kopfbalken des Kastenzweigs: eine \cs{rlap}\cs{smash}-\cs{rule}
%   genau in Höhe der (durch einen unsichtbaren Strut aufgepolsterten)
%   Titelzeile -- so schliesst der Balken bündig mit der Kastenoberkante ab.
%   Die Artefakt-Markierung übernimmt der Aufrufer
%   (\cs{__osgstyler_block_head_artifact:n}). \code{#1} ist seine
%   Breite.}{The boxed branch's
%   head bar: an \cs{rlap}\cs{smash} \cs{rule} exactly as tall as the title
%   line (padded by an invisible strut), so it meets the box's top edge
%   flush. The caller marks it as artifact
%   (\cs{__osgstyler_block_head_artifact:n}). \code{#1} is its width.}
\cs_new_protected:Npn \__osgstyler_block_headbar:n #1
  {
    \str_if_eq:VnF \l__osgstyler_block_head_bg_tl { none }
      {
        \bool_lazy_and:nnTF
          { \dim_compare_p:nNn \l__osgstyler_block_corner_radius_dim > \c_zero_dim }
          { \cs_if_exist_p:N \pgfpicture }
          { \__osgstyler_block_headbar_round:n {#1} }
          {
            \rlap { \smash {
              \color { \OsgColorName{ \l__osgstyler_block_head_bg_tl } }
              \rule
                [ \dim_eval:n { -\dp\strutbox
                                - \l__osgstyler_block_head_padding_bottom_dim } ]
                { \dim_eval:n { #1 } }
                { \dim_eval:n { \ht\strutbox + \dp\strutbox
                                + \l__osgstyler_block_head_padding_top_dim
                                + \l__osgstyler_block_head_padding_bottom_dim } }
            } }%
          }
      }
  }
% \ldeen{Kopfbalken mit abgerundeten \emph{oberen} Ecken (Radius wie der
%   Kasten), damit er in einem gerundeten \code{titlebar}-Kasten nicht über
%   die Ecken hinaussteht.}{Head bar
%   with rounded \emph{top} corners (the box's radius) so it does not
%   overshoot the corners inside a rounded \code{titlebar} box.}
\cs_new_protected:Npn \__osgstyler_block_headbar_round:n #1
  {
    \rlap { \smash {
      \begin { pgfpicture }
        \pgfsetbaseline { 0pt }
        \pgfsetfillcolor { \OsgColorName { \l__osgstyler_block_head_bg_tl } }
        \dim_set:Nn \l_tmpa_dim
          { \ht\strutbox + \l__osgstyler_block_head_padding_top_dim }
        \dim_set:Nn \l_tmpb_dim
          { -\dp\strutbox - \l__osgstyler_block_head_padding_bottom_dim }
        \pgfpathmoveto { \pgfpoint { 0pt } { \l_tmpb_dim } }
        \pgfpathlineto { \pgfpoint { 0pt }
          { \dim_eval:n { \l_tmpa_dim - \l__osgstyler_block_corner_radius_dim } } }
        \pgfpatharc { 180 } { 90 } { \l__osgstyler_block_corner_radius_dim }
        \pgfpathlineto { \pgfpoint
          { \dim_eval:n { (#1) - \l__osgstyler_block_corner_radius_dim } }
          { \l_tmpa_dim } }
        \pgfpatharc { 90 } { 0 } { \l__osgstyler_block_corner_radius_dim }
        \pgfpathlineto { \pgfpoint { \dim_eval:n {#1} } { \l_tmpb_dim } }
        \pgfpathclose
        \pgfusepath { fill }
      \end { pgfpicture }
    } }%
  }
% \ldeen{\code{titled} mit getönter Body-Fläche: ein Kasten
%   (\cs{fcolorbox}+\env{minipage}, ein \code{Div}). Innenabstand von Hand
%   (\cs{fboxsep} ist Null), damit der Kopfbalken randlos an die Kanten
%   läuft und bündig mit der Oberkante abschliesst.}{\code{titled} with a
%   tinted body area: a box (\cs{fcolorbox}+\env{minipage}, one \code{Div}).
%   Padding done by hand (\cs{fboxsep} is zero) so the head bar bleeds to
%   the edges and meets the top edge flush.}
\cs_new_protected:Npn \__osgstyler_block_typeset_titled_box:n #1
  {
    \par
    \group_begin:
      \__osgstyler_block_box_para_off:
      \addvspace { \medskipamount }
      \setlength \fboxsep { 0pt }
      \setlength \fboxrule { \l__osgstyler_block_rule_dim }
      \noindent
      \__osgstyler_block_fbox:nnn
        { \OsgColorName { \l__osgstyler_block_rule_color_tl } }
        { \__osgstyler_block_bg:V \l__osgstyler_block_body_bg_tl }
        {
          \begin { minipage } { \dim_eval:n { \linewidth - 2\fboxrule } }
            \tl_if_empty:NF \l__osgstyler_block_title_tl
              {
                \noindent
                % invisible strut sets the title line's metrics; the bar
                % (smashed) is drawn to the same extent.
                \__osgstyler_block_head_artifact:n
                  {
                    \rule
                      [ \dim_eval:n { -\dp\strutbox
                                      - \l__osgstyler_block_head_padding_bottom_dim } ]
                      { 0pt }
                      { \dim_eval:n { \ht\strutbox + \dp\strutbox
                                      + \l__osgstyler_block_head_padding_top_dim
                                      + \l__osgstyler_block_head_padding_bottom_dim } }%
                    \__osgstyler_block_headbar:n { \linewidth }
                  }
                \skip_horizontal:n { \l__osgstyler_block_head_padding_left_dim }
                \dim_set:Nn \rightskip { \l__osgstyler_block_head_padding_right_dim }
                { \color { \OsgColorName{ \l__osgstyler_block_head_fg_tl } }
                  \l__osgstyler_block_head_font_tl
                  \l__osgstyler_block_title_tl \par }
                \nobreak
                \skip_vertical:n { \l__osgstyler_block_body_padding_top_dim }
              }
            \dim_set:Nn \leftskip  { \l__osgstyler_block_body_padding_left_dim }
            \dim_set:Nn \rightskip { \l__osgstyler_block_body_padding_right_dim }
            \tl_if_empty:NT \l__osgstyler_block_title_tl
              { \tex_kern:D \l__osgstyler_block_body_padding_top_dim }
            % Do not force horizontal mode here: if the body starts with a
            % list, \noindent would create an empty paragraph before it.
            \setlength \parindent { 0pt }
            \ignorespaces #1
            \par
            % A kern, not \skip_vertical: \endminipage's \unskip would drop
            % trailing glue and leave the body sitting on the bottom edge.
            \tex_kern:D \l__osgstyler_block_body_padding_bottom_dim
          \end { minipage }
        }%
      \par
    \group_end:
    \addvspace { \medskipamount }
  }
\cs_new_protected:Npn \__osgstyler_block_typeset_titled_flow:n #1
  {
    \par
    \addvspace { \medskipamount }
    \group_begin:
      \tl_if_empty:NF \l__osgstyler_block_title_tl
        {
          % Optional rule above the head (booktabs-style bracket top).
          \dim_compare:nNnT \l__osgstyler_block_head_rule_dim > \c_zero_dim
            {
              \__osgstyler_block_rule:nn
                { \l__osgstyler_block_head_rule_color_tl }
                { \l__osgstyler_block_head_rule_dim }
              \nointerlineskip
              \skip_vertical:n { 0.2 \baselineskip }
            }
          % The coloured strip (if any) sits behind the title's own line.
          \skip_vertical:n { \l__osgstyler_block_head_padding_top_dim }
          \noindent
          \__osgstyler_block_head_artifact:n
            {
              \__osgstyler_block_headstrip:nn
                { -\l__osgstyler_block_head_padding_left_dim }
                { \linewidth + \l__osgstyler_block_head_padding_left_dim
                             + \l__osgstyler_block_head_padding_right_dim }
            }
          {
            \skip_horizontal:n { \l__osgstyler_block_head_padding_left_dim }
            \dim_set:Nn \rightskip { \l__osgstyler_block_head_padding_right_dim }
            \color { \OsgColorName{ \l__osgstyler_block_head_fg_tl } }
            \l__osgstyler_block_head_font_tl
            \l__osgstyler_block_title_tl
          }%
          \par
          \skip_vertical:n { \l__osgstyler_block_head_padding_bottom_dim }
          \skip_vertical:n { \l__osgstyler_block_body_padding_top_dim }
          % Normal interline spacing to the body -- one line apart, like
          % beamer's default block.  The head strip is \cs{smash}ed and
          % reaches only ~0.3\baselineskip below the title baseline, so it
          % does not touch the body at this distance.
        }
      \group_begin:
        \dim_set:Nn \leftskip  { \l__osgstyler_block_body_padding_left_dim }
        \dim_set:Nn \rightskip { \l__osgstyler_block_body_padding_right_dim }
        % Keep list-like bodies in vertical mode while ordinary text still
        % starts flush with the body edge.
        \setlength \parindent { 0pt }
        \ignorespaces #1 \par
        \skip_vertical:n { \l__osgstyler_block_body_padding_bottom_dim }
      \group_end:
      % Optional rule below the body (bracket bottom).
      \dim_compare:nNnT \l__osgstyler_block_foot_rule_dim > \c_zero_dim
        {
          \nointerlineskip
          \skip_vertical:n { 0.35 \baselineskip }
          \__osgstyler_block_rule:nn
            { \l__osgstyler_block_foot_rule_color_tl }
            { \l__osgstyler_block_foot_rule_dim }
        }
    \group_end:
    \addvspace { \medskipamount }
  }

% \ldeen*{Instanzen sind bei xtemplate ohnehin nur gespeichertes
% Keyval-Material -- Rahmen/Schatten/Ecken lassen sich also schon durch
% simples Verketten mehrerer Fragmente kombinieren, ohne dass es dafür
% je eine eigene Instanz bräuchte. @1 gibt diesem Verketten nur einen
% Namen: der Schlüssel @2 schlägt benannte, mit @3 deklarierte
% Fragmente nach und reiht sie vor die übrigen, unverändert
% durchgereichten Schlüssel ein.}
% {Instances are just stored keyval material in xtemplate to begin
% with -- rule/shadow/corner combinations can already be composed by
% concatenating fragments, without a dedicated instance for each
% combination. @1 merely gives that concatenation a name: the @2 key
% looks up named fragments declared via @3 and prepends them to the
% remaining keys, which pass through unchanged.}
% {\cs{DeclareOsgBlockInstance}}{traits}{\cs{DeclareOsgBlockTrait}}

\prop_new:N \g__osgstyler_block_trait_prop
\tl_new:N \l__osgstyler_block_instance_keys_tl
\tl_new:N \l__osgstyler_block_trait_tl

\msg_new:nnnn { osgstyler } { unknown-block-trait }
  { Unknown~block~trait~'#1'. }
  { Declare~it~with~\token_to_str:N\DeclareOsgBlockTrait~before~using~it. }

\NewDocumentCommand \DeclareOsgBlockTrait { m m }
  { \prop_gput:Nne \g__osgstyler_block_trait_prop {#1} { \exp_not:n {#2} } }

\keys_define:nn { osgstyler / block-instance }
  {
    % \ldeen{Ein eigener Scratch, nicht \cs{l_tmpa_tl}: der Aufrufer kann
    %   \cs{l_tmpa_tl} über den \cs{keys_set:nn}"=Aufruf hinweg brauchen.}{A
    %   private scratch, not \cs{l_tmpa_tl}: the caller may rely on
    %   \cs{l_tmpa_tl} across the \cs{keys_set:nn} call.}
    traits .code:n =
      \clist_map_inline:nn {#1}
        {
          % \ldeen{\cs{tl_put_right:NV} statt \cs{tl_put_right:Nx}: der
          %   Trait-Wert darf \cs{normalfont} u.\,ä.\ enthalten, die eine
          %   vollständige Expansion sprengen würden.}{\cs{tl_put_right:NV}
          %   not \cs{tl_put_right:Nx}: a trait value may contain
          %   \cs{normalfont} and the like, which would break full
          %   expansion.}
          \prop_get:NnNTF \g__osgstyler_block_trait_prop {##1}
            \l__osgstyler_block_trait_tl
            {
              \tl_put_right:NV \l__osgstyler_block_instance_keys_tl
                \l__osgstyler_block_trait_tl
              \tl_put_right:Nn \l__osgstyler_block_instance_keys_tl { , }
            }
            { \msg_error:nnn { osgstyler } { unknown-block-trait } {##1} }
        },
    unknown .code:n =
      \tl_put_right:Nx \l__osgstyler_block_instance_keys_tl
        { \exp_not:V \l_keys_key_str = \exp_not:n {#1} , }
  }

\cs_new_protected:Npn \__osgstyler_block_instance_resolve:n #1
  {
    \tl_clear:N \l__osgstyler_block_instance_keys_tl
    \keys_set:nn { osgstyler / block-instance } {#1}
  }

% \ldeen*{Merkt sich pro Instanz, ob \code{layout=titled} wirksam ist
% (direkt oder über einen Trait). Umhüllende Pakete wie \pkg*{lttheme}
% brauchen das, weil sie einen Kopf-Titel dann anders zuführen müssen als
% einen dem Body vorangestellten. Ein Aufruf ohne Layout-Angabe lässt den
% Vermerk stehen -- \cs{StyleOsgBlockInstance} ist additiv.}
% {Records per instance whether \code{layout=titled} is in effect
% (directly or through a trait). Wrapping packages such as \pkg*{lttheme}
% need this because a head title has to be fed in differently from one
% prepended to the body. A call without a layout key leaves the record
% untouched -- \cs{StyleOsgBlockInstance} is additive.}
\prop_new:N \g__osgstyler_block_titled_prop

\cs_new_protected:Npn \__osgstyler_block_record_layout:n #1
  {
    \tl_if_in:NnT \l__osgstyler_block_instance_keys_tl { titled }
      { \prop_gput:Nnn \g__osgstyler_block_titled_prop {#1} { } }
    \tl_if_in:NnT \l__osgstyler_block_instance_keys_tl { plain }
      { \prop_gremove:Nn \g__osgstyler_block_titled_prop {#1} }
  }

\prg_new_conditional:Npnn \osgstyler_if_block_titled:n #1 { p, T, F, TF }
  {
    \prop_if_in:NnTF \g__osgstyler_block_titled_prop {#1}
      { \prg_return_true: } { \prg_return_false: }
  }
\cs_new_eq:NN \OsgIfBlockTitledT \osgstyler_if_block_titled:nT
\cs_new_eq:NN \OsgIfBlockTitledF \osgstyler_if_block_titled:nF
\cs_new_eq:NN \OsgIfBlockTitledTF \osgstyler_if_block_titled:nTF

% xtemplate's own key parser, like enumitem's \setlist (see
% \cs{__osgstyler_list_apply_level:nnn}), scans its keys argument
% before expansion; a bare macro reference there is read as one
% valueless key, not expanded first. Building the whole call as
% already-expanded literal text avoids that.
\tl_new:N \l__osgstyler_block_instance_call_tl

% \ldeen{\cs{exp_not:V} um die gesammelten Schlüssel: sie dürfen
%   \cs{normalfont} u.\,ä.\ enthalten (etwa aus \code{head-font}), die eine
%   vollständige Expansion sprengen würden.}{\cs{exp_not:V} around the
%   collected keys: they may contain \cs{normalfont} and the like (e.g.\ from
%   \code{head-font}), which would break full expansion.}
\NewDocumentCommand \DeclareOsgBlockInstance { m m }
  {
    \__osgstyler_block_instance_resolve:n {#2}
    \__osgstyler_block_record_layout:n {#1}
    \tl_set:Ne \l__osgstyler_block_instance_call_tl
      {
        \exp_not:N \DeclareInstance { osgstyler-block }
          { \exp_not:n {#1} } { standard }
          { \exp_not:V \l__osgstyler_block_instance_keys_tl }
      }
    \tl_use:N \l__osgstyler_block_instance_call_tl
  }

\NewDocumentCommand \StyleOsgBlockInstance { m m }
  {
    \__osgstyler_block_instance_resolve:n {#2}
    \__osgstyler_block_record_layout:n {#1}
    \tl_set:Ne \l__osgstyler_block_instance_call_tl
      {
        \exp_not:N \EditInstance { osgstyler-block }
          { \exp_not:n {#1} }
          { \exp_not:V \l__osgstyler_block_instance_keys_tl }
      }
    \tl_use:N \l__osgstyler_block_instance_call_tl
  }

% ==========================================
% NUMBERING TEMPLATE
% ==========================================
%
% \ldeen*{Liefert ausschließlich die formatierte Zahl (z.\,B. "`2.3"');
% weder ein lokalisiertes Label wie "`Satz"' noch ein optionaler
% Autoren-Titel gehören dazu -- beides bleibt Sache der Umgebung, die
% eine Instanz dieses Templates verwendet, damit dieses Paket
% sprachneutral bleibt. @1 registriert den zugrundeliegenden
% \LaTeX-Zähler einmalig (\cs{DeclareOsgNumbering} löst dies sofort
% aus, nicht erst bei der ersten Verwendung), da ein zweiter Aufruf von
% \cs{newcounter} sonst fehlschlüge; Zählername und Format-Vorgabe
% werden dabei zusätzlich unter dem Instanznamen gemerkt, weil weder
% \cs{StepOsgNumbering} noch \cs{OsgNumberingValue} sonst einen Weg
% hätten, sie wiederzufinden.}
% {Produces only the formatted number (e.g. "`2.3"'); neither a
% localized label such as "`Theorem"' nor an optional author-supplied
% title belong here -- both remain the calling environment's concern,
% keeping this package language-neutral. @1 registers the underlying
% \LaTeX\ counter exactly once (\cs{DeclareOsgNumbering} triggers this
% immediately, not on first use), since a second \cs{newcounter} call
% would otherwise fail; the counter name and format are additionally
% remembered under the instance name, since neither
% \cs{StepOsgNumbering} nor \cs{OsgNumberingValue} would otherwise have
% a way to recover them.}
% {\cs{DeclareOsgNumbering}}

\tl_new:N \l__osgstyler_numbering_counter_tl
\tl_new:N \l__osgstyler_numbering_within_tl
\tl_new:N \l__osgstyler_numbering_format_tl
\prop_new:N \g__osgstyler_numbering_counter_prop
\prop_new:N \g__osgstyler_numbering_format_prop

\NewTemplateType { osgstyler-numbering } { 1 }
\DeclareTemplateInterface { osgstyler-numbering } { standard } { 1 }
  {
    counter : tokenlist =,
    within  : tokenlist = none,
    format  : tokenlist =
  }
\DeclareTemplateCode { osgstyler-numbering } { standard } { 1 }
  {
    counter = \l__osgstyler_numbering_counter_tl,
    within  = \l__osgstyler_numbering_within_tl,
    format  = \l__osgstyler_numbering_format_tl
  }
  { \__osgstyler_numbering_register:n {#1} }

\cs_new_protected:Npn \__osgstyler_numbering_register:n #1
  {
    \cs_if_exist:cF { c@ \l__osgstyler_numbering_counter_tl }
      {
        \str_if_eq:VnTF \l__osgstyler_numbering_within_tl { none }
          { \newcounter { \l__osgstyler_numbering_counter_tl } }
          {
            \newcounter
              { \l__osgstyler_numbering_counter_tl }
              [ \l__osgstyler_numbering_within_tl ]
          }
      }
    \prop_gput:NnV \g__osgstyler_numbering_counter_prop {#1}
      \l__osgstyler_numbering_counter_tl
    \prop_gput:NnV \g__osgstyler_numbering_format_prop {#1}
      \l__osgstyler_numbering_format_tl
  }

\NewDocumentCommand \DeclareOsgNumbering { m m }
  {
    \DeclareInstance { osgstyler-numbering } {#1} { standard } {#2}
    \UseInstance { osgstyler-numbering } {#1} {#1}
  }

\NewExpandableDocumentCommand \OsgNumberingCounter { m }
  { \prop_item:Nn \g__osgstyler_numbering_counter_prop {#1} }

\NewDocumentCommand \StepOsgNumbering { m }
  { \refstepcounter { \OsgNumberingCounter {#1} } }

\NewExpandableDocumentCommand \OsgNumberingValue { m }
  { \prop_item:Nn \g__osgstyler_numbering_format_prop {#1} }

% ==========================================
% NEW ENVIRONMENTS FROM SCRATCH
% ==========================================
%
% \ldeen*{@1 deckt den Fall ab, in dem \emph{keine} bereits bestehende
% Umgebung umhüllt wird (der Gegenfall -- ein bekanntes Environment wie
% \env{block} oder ein von \pkg*{amsthm} erzeugtes \env{theorem} nur
% optisch einzurahmen -- bleibt eine dokumentierte, pro Environment
% wiederholte Anwendung von \cs{UseInstance}\Marg{osgstyler-block}, da
% sich die Argumentsignaturen bestehender Environments zu sehr
% unterscheiden, um sie generisch zu erfassen). @2 selbst bleibt
% sprachneutral: das Label (@3) liefert der Aufrufer als fertigen Text,
% \pkg*{osgstyler} übersetzt nicht.}
% {@1 covers the case where \emph{no} pre-existing environment is being
% wrapped (the opposite case -- giving a known environment such as
% \env{block} or an \pkg*{amsthm}-generated \env{theorem} only a visual
% frame -- remains a documented, per-environment repetition of
% \cs{UseInstance}\Marg{osgstyler-block}, since existing environments'
% argument signatures differ too much to capture generically). @2 itself
% stays language-neutral: the caller supplies the label (@3) as
% ready-made text, \pkg*{osgstyler} does not translate.}
% {\cs{NewOsgStyledEnvironment}}{\cs{DeclareOsgNumbering}}

\tl_new:N \l__osgstyler_styled_env_title_tl

\cs_new_protected:Npn \__osgstyler_styled_env_begin:nnnnn #1#2#3#4#5
  {
    \group_begin:
      \tl_clear:N \l__osgstyler_styled_env_title_tl
      \tl_put_right:Nn \l__osgstyler_styled_env_title_tl {#3}
      \str_if_eq:nnF {#2} { none }
        {
          \StepOsgNumbering {#2}
          \tl_put_right:Nn \l__osgstyler_styled_env_title_tl
            { ~\OsgNumberingValue {#2} }
        }
      \IfNoValueF {#4}
        { \tl_put_right:Nn \l__osgstyler_styled_env_title_tl { ~(#4)} }
      \__osgstyler_block_use:nnn {#1}
        { \tl_use:N \l__osgstyler_styled_env_title_tl } { #5 }
    \group_end:
  }

% \ldeen*{Ist @1 (aus \pkg*{osglecture-modes}) beim Aufruf bereits
% geladen, bekommt das erzeugte Environment zusätzlich eine führende
% \cs{d<>}-Spezifikation: ein inaktiver Modus unterdrückt die Ausgabe
% vollständig, \emph{einschließlich} des Nummerierungsschritts --
% ausgeblendeter Inhalt soll keine Lücke in der Nummerierung
% hinterlassen. Das ist hier gefahrlos möglich (anders als bei
% nativen Overlay-Umgebungen ohne Fallback), weil der Textkörper
% ohnehin als \cs{+b}-Argument vollständig eingelesen wird, bevor
% irgendetwas davon ausgeführt wird -- ihn bedingt zu verwerfen ersetzt
% keine bereits laufende Ausgabe.}
% {If @1 (from \pkg*{osglecture-modes}) is already loaded when this
% runs, the generated environment additionally gets a leading
% \cs{d<>} specification: an inactive mode suppresses output entirely,
% \emph{including} the numbering step -- hidden content should not
% leave a gap in the numbering. This is safe here (unlike native
% overlay environments without a fallback) because the body is fully
% read as a \cs{+b} argument before any of it executes -- discarding it
% conditionally does not interrupt output already under way.}
% {\pkg*{osglecture-modes}}
\cs_new_protected:Npn \__osgstyler_styled_env_mode_dispatch:nnnnnn #1#2#3#4#5#6
  {
    \IfNoValueTF {#4}
      { \__osgstyler_styled_env_begin:nnnnn {#1}{#2}{#3}{#5}{#6} }
      {
        \IfLectureModeT {#4}
          { \__osgstyler_styled_env_begin:nnnnn {#1}{#2}{#3}{#5}{#6} }
      }
  }

% \ldeen*{Ab @1 (tcolorbox-artig, wie \pkg*{tcolorbox}s eigenes
% \cs{newtcolorbox}\oarg{nargs}) verlangt das erzeugte Environment
% @1 Pflichtargumente statt des optionalen \Marg{Name}; die Titelvorlage
% (@2) wird dann pro Verwendung mit den echten Argumenten gefüllt --
% genau Beamers eigene \env{block}-Signatur \Verb+\begin{block}{Titel}+
% lässt sich damit für die Langform nachbilden (@3). Die automatische
% Label+Zahl+Name-Komposition entfällt hier bewusst: wer Nummerierung
% mit freiem Titel kombinieren will, bezieht \cs{OsgNumberingValue}
% selbst in die Vorlage ein.}
% {From @1 onward (tcolorbox-style, like \pkg*{tcolorbox}'s own
% \cs{newtcolorbox}\oarg{nargs}) the generated environment requires
% @1 mandatory arguments instead of the optional \Marg{Name}; the title
% template (@2) is then filled with the real arguments on each use --
% this reproduces beamer's own \env{block} signature
% \Verb+\begin{block}{Title}+ for the long form (@3). The automatic
% label+number+name composition is deliberately absent here: combining
% numbering with a free title means including \cs{OsgNumberingValue}
% in the template yourself.}
% {\code{1}}{title~template}{\NewOsgStyledEnvironment\Marg{block}\Marg{block}\Marg{none}\oarg{1}\Marg{\#1}}
\msg_new:nnnn { osgstyler } { unsupported-styled-env-nargs }
  { Styled~environments~support~0--3~title~arguments,~not~'#1'. }
  { Use~a~value~from~0~to~3~for~the~optional~argument~of
    ~\token_to_str:N\NewOsgStyledEnvironment. }

\cs_new_protected:Npn \__osgstyler_styled_env_freetitle_render:nnn #1#2#3
  {
    \group_begin:
      \__osgstyler_block_use:nnn {#1} { #2 } { #3 }
    \group_end:
  }

\NewDocumentCommand \NewOsgStyledEnvironment { m m m O{0} m }
  {
    \int_compare:nNnTF {#4} = { 0 }
      {
        \cs_if_exist:NTF \IfLectureModeTF
          {
            \NewDocumentEnvironment {#1} { d<> o +b }
              {
                \__osgstyler_styled_env_mode_dispatch:nnnnnn
                  {#2}{#3}{#5}{##1}{##2}{##3}
              }
              { }
          }
          {
            \NewDocumentEnvironment {#1} { o +b }
              { \__osgstyler_styled_env_begin:nnnnn {#2}{#3}{#5}{##1}{##2} }
              { }
          }
      }
      {
        \int_case:nnF {#4}
          {
            { 1 }
              {
                \cs_set:cpn { __osgstyler_styled_env_title_apply_#1:n } ##1
                  { #5 }
                \cs_if_exist:NTF \IfLectureModeTF
                  {
                    \NewDocumentEnvironment {#1} { d<> m +b }
                      {
                        \IfNoValueTF {##1}
                          {
                            \tl_set:Ne \l__osgstyler_styled_env_title_tl
                              {
                                \exp_not:c { __osgstyler_styled_env_title_apply_#1:n }
                                  {##2}
                              }
                            \__osgstyler_styled_env_freetitle_render:nnn {#2}
                              { \tl_use:N \l__osgstyler_styled_env_title_tl } {##3}
                          }
                          {
                            \IfLectureModeT {##1}
                              {
                                \tl_set:Ne \l__osgstyler_styled_env_title_tl
                                  {
                                    \exp_not:c
                                      { __osgstyler_styled_env_title_apply_#1:n }
                                      {##2}
                                  }
                                \__osgstyler_styled_env_freetitle_render:nnn {#2}
                                  { \tl_use:N \l__osgstyler_styled_env_title_tl } {##3}
                              }
                          }
                      }
                      { }
                  }
                  {
                    \NewDocumentEnvironment {#1} { m +b }
                      {
                        \tl_set:Ne \l__osgstyler_styled_env_title_tl
                          { \exp_not:c { __osgstyler_styled_env_title_apply_#1:n } {##1} }
                        \__osgstyler_styled_env_freetitle_render:nnn {#2}
                          { \tl_use:N \l__osgstyler_styled_env_title_tl } {##2}
                      }
                      { }
                  }
              }
            { 2 }
              {
                \cs_set:cpn { __osgstyler_styled_env_title_apply_#1:nn } ##1##2
                  { #5 }
                \cs_if_exist:NTF \IfLectureModeTF
                  {
                    \NewDocumentEnvironment {#1} { d<> m m +b }
                      {
                        \IfNoValueTF {##1}
                          {
                            \tl_set:Ne \l__osgstyler_styled_env_title_tl
                              {
                                \exp_not:c { __osgstyler_styled_env_title_apply_#1:nn }
                                  {##2}{##3}
                              }
                            \__osgstyler_styled_env_freetitle_render:nnn {#2}
                              { \tl_use:N \l__osgstyler_styled_env_title_tl } {##4}
                          }
                          {
                            \IfLectureModeT {##1}
                              {
                                \tl_set:Ne \l__osgstyler_styled_env_title_tl
                                  {
                                    \exp_not:c
                                      { __osgstyler_styled_env_title_apply_#1:nn }
                                      {##2}{##3}
                                  }
                                \__osgstyler_styled_env_freetitle_render:nnn {#2}
                                  { \tl_use:N \l__osgstyler_styled_env_title_tl } {##4}
                              }
                          }
                      }
                      { }
                  }
                  {
                    \NewDocumentEnvironment {#1} { m m +b }
                      {
                        \tl_set:Ne \l__osgstyler_styled_env_title_tl
                          {
                            \exp_not:c { __osgstyler_styled_env_title_apply_#1:nn }
                              {##1}{##2}
                          }
                        \__osgstyler_styled_env_freetitle_render:nnn {#2}
                          { \tl_use:N \l__osgstyler_styled_env_title_tl } {##3}
                      }
                      { }
                  }
              }
            { 3 }
              {
                \cs_set:cpn { __osgstyler_styled_env_title_apply_#1:nnn } ##1##2##3
                  { #5 }
                \cs_if_exist:NTF \IfLectureModeTF
                  {
                    \NewDocumentEnvironment {#1} { d<> m m m +b }
                      {
                        \IfNoValueTF {##1}
                          {
                            \tl_set:Ne \l__osgstyler_styled_env_title_tl
                              {
                                \exp_not:c { __osgstyler_styled_env_title_apply_#1:nnn }
                                  {##2}{##3}{##4}
                              }
                            \__osgstyler_styled_env_freetitle_render:nnn {#2}
                              { \tl_use:N \l__osgstyler_styled_env_title_tl } {##5}
                          }
                          {
                            \IfLectureModeT {##1}
                              {
                                \tl_set:Ne \l__osgstyler_styled_env_title_tl
                                  {
                                    \exp_not:c
                                      { __osgstyler_styled_env_title_apply_#1:nnn }
                                      {##2}{##3}{##4}
                                  }
                                \__osgstyler_styled_env_freetitle_render:nnn {#2}
                                  { \tl_use:N \l__osgstyler_styled_env_title_tl } {##5}
                              }
                          }
                      }
                      { }
                  }
                  {
                    \NewDocumentEnvironment {#1} { m m m +b }
                      {
                        \tl_set:Ne \l__osgstyler_styled_env_title_tl
                          {
                            \exp_not:c { __osgstyler_styled_env_title_apply_#1:nnn }
                              {##1}{##2}{##3}
                          }
                        \__osgstyler_styled_env_freetitle_render:nnn {#2}
                          { \tl_use:N \l__osgstyler_styled_env_title_tl } {##4}
                      }
                      { }
                  }
              }
          }
          { \msg_error:nnn { osgstyler } { unsupported-styled-env-nargs } {#4} }
      }
  }

% ==========================================
% STYLING KNOWN ENVIRONMENTS (CASE A, WITHOUT OVERLAYS)
% ==========================================
%
% \ldeen*{@1 verallgemeinert das Muster, mit dem \pkg*{lttheme}
% \env{block} umhüllt, für Environments \emph{ohne} Overlay-Syntax --
% deren Argumentsignatur ist einheitlich genug (ein optionaler Name vor
% einem Textkörper), um sie generisch zu erfassen, anders als bei
% \env{block}/\env{alertblock}, die \cs{d<>} brauchen und daher
% themenspezifisch bleiben. Das optionale Argument wird unverändert an
% das umhüllte Original weitergereicht, nie selbst interpretiert --
% @2s eigene \Marg{Name}-Klammer vor \env{theorem}-artigen Environments
% (\cs{newtheorem}) funktioniert dadurch unverändert, samt eigener
% Nummerierung: @1 fügt nur die Box-Optik um @2s vollständige Ausgabe
% herum, ohne dessen Zähler oder Kopf-Rendering zu berühren.}
% {@1 generalizes the pattern \pkg*{lttheme} uses to wrap \env{block}
% for environments \emph{without} overlay syntax -- their argument
% signature is uniform enough (an optional name before a text body) to
% capture generically, unlike \env{block}/\env{alertblock}, which need
% \cs{d<>} and therefore stay theme-specific. The optional argument is
% forwarded to the wrapped original unchanged, never interpreted here
% itself -- @2's own \Marg{name} bracket in front of \env{theorem}-like
% environments (\cs{newtheorem}) therefore keeps working unchanged,
% numbering included: @1 only adds the box styling around @2's complete
% output, without touching its counter or heading rendering.}
% {\cs{BindOsgBlockEnvironment}}{\pkg*{amsthm}}

\NewDocumentCommand \BindOsgBlockEnvironment { m m }
  { \__osgstyler_block_bind_environment:nn {#1}{#2} }

\cs_new_protected:Npn \__osgstyler_block_render_bound:nnnn #1#2#3#4
  {
    \__osgstyler_block_use:nnn {#2} { }
      {
        \IfNoValueTF {#3}
          { \begin{osgstyler-original-#1} #4 \end{osgstyler-original-#1} }
          { \begin{osgstyler-original-#1}[#3] #4 \end{osgstyler-original-#1} }
      }
  }

% See \cs{NewOsgStyledEnvironment} for why discarding the body
% conditionally is safe here.
\cs_new_protected:Npn \__osgstyler_block_bound_mode_dispatch:nnnnn #1#2#3#4#5
  {
    \IfNoValueTF {#3}
      { \__osgstyler_block_render_bound:nnnn {#1}{#2}{#4}{#5} }
      {
        \IfLectureModeT {#3}
          { \__osgstyler_block_render_bound:nnnn {#1}{#2}{#4}{#5} }
      }
  }

\cs_new_protected:Npn \__osgstyler_block_bind_environment:nn #1#2
  {
    \NewEnvironmentCopy { osgstyler-original-#1 } {#1}
    \cs_if_exist:NTF \IfLectureModeTF
      {
        \RenewDocumentEnvironment {#1} { d<> o +b }
          {
            \__osgstyler_block_bound_mode_dispatch:nnnnn
              {#1}{#2}{##1}{##2}{##3}
          }
          { }
      }
      {
        \RenewDocumentEnvironment {#1} { o +b }
          { \__osgstyler_block_render_bound:nnnn {#1}{#2}{##1}{##2} }
          { }
      }
  }

% \ldeen*{Die \cls*{LaTeX}-Kernumgebungen @1/@2 sind auf jedem Ziel
% verfügbar, weshalb ihre Instanzen -- unsichtbar, wie die
% Schnittstellen-Vorgaben es ohnehin vorsehen -- immer deklariert sind.
% Umgebunden werden sie aber erst durch einen bewussten Aufruf von
% \cs{BindOsgBlockEnvironment} oder @3, nicht beim Laden dieses Pakets:
% wer \pkg*{osgstyler} nur wegen Farben oder Inline-Befehlen lädt, soll
% nicht überrascht feststellen, dass sich seine Zitate verändert
% haben.}
% {The \cls*{LaTeX} kernel environments @1/@2 are available on every
% target, which is why their instances -- invisible, as the interface
% defaults already provide anyway -- are always declared. They are only
% actually rebound by a deliberate call to \cs{BindOsgBlockEnvironment}
% or @3, not on loading this package: someone loading \pkg*{osgstyler}
% only for colors or inline commands should not be surprised to find
% their quotations changed.}
% {\env{quote}}{\env{quotation}}{\cs{UseOsgStandardBlockEnvironments}}
\DeclareInstance { osgstyler-block } { quote } { standard } { }
\DeclareInstance { osgstyler-block } { quotation } { standard } { }

% \ldeen*{@1/@2/@3 reproduzieren Beamers eigene, freie Titelsignatur
% (\Verb+\begin{block}{Titel}+) über @4 -- dieselbe Quelle kompiliert
% damit unter Beamer/ltx-talk (dort natives Environment, siehe
% \pkg*{lttheme}) wie unter der Langform (hier neu erzeugt) gleich.
% Nur wenn der Name noch nicht existiert wird das Environment
% angelegt: existiert es schon nativ, ändert @5 daran nichts.}
% {@1/@2/@3 reproduce beamer's own free-title signature
% (\Verb+\begin{block}{Title}+) via @4 -- the same source therefore
% compiles the same way under beamer/ltx-talk (native environment
% there, see \pkg*{lttheme}) and under the long form (newly created
% here). The environment is only created if the name does not exist
% yet: if it is already native, @5 leaves it untouched.}
% {\env{block}}{\env{alertblock}}{\env{exampleblock}}
% {\cs{NewOsgStyledEnvironment}}{\cs{UseOsgStandardBlockEnvironments}}
\DeclareInstance { osgstyler-block } { block } { standard }
  { rule-thickness = .4pt, padding = .5em }
\DeclareInstance { osgstyler-block } { alertblock } { standard }
  { rule-thickness = .4pt, padding = .5em, rule-color = alert }
\DeclareInstance { osgstyler-block } { exampleblock } { standard }
  { rule-thickness = .4pt, padding = .5em, rule-color = example }

\NewDocumentCommand \UseOsgStandardBlockEnvironments { }
  {
    \BindOsgBlockEnvironment { quote } { quote }
    \BindOsgBlockEnvironment { quotation } { quotation }
    \cs_if_exist:cF { block }
      { \NewOsgStyledEnvironment { block } { block } { none } [ 1 ] { ##1 } }
    \cs_if_exist:cF { alertblock }
      { \NewOsgStyledEnvironment { alertblock } { alertblock } { none } [ 1 ] { ##1 } }
    \cs_if_exist:cF { exampleblock }
      { \NewOsgStyledEnvironment { exampleblock } { exampleblock } { none } [ 1 ] { ##1 } }
  }

% \ldeen*{Ein weiteres @1-Accessoire: eine dünne, akzentfarbene Linie
% vor und nach dem Inhalt statt eines vollen Rahmens (\code{rule-thickness
% = 0pt} erzwingt das, unabhängig vom bisherigen Zustand der Instanz) --
% passend sowohl für Zitate als auch für theorem-artige Umgebungen ohne
% eigenen Kasten (\cls*{amsthm} rahmt seine Umgebungen ja auch nicht
% ein). Ein \cs{traits}-Wert durchläuft auf dem Weg von der Deklaration
% bis zur fertigen Instanz mehrere vollständige Expansionsrunden (siehe
% \cs{__osgstyler_block_instance_resolve:n}); Befehle mit eigenem
% Lookahead auf das nächste Zeichen (\cs{rule}s optionales
% Anhebungs-Argument, \cs{vspace}, \cs{newline}, \cs{par} innerhalb
% eines nicht-\cs{long}-Arguments von \pkg*{l3keys}) überstehen das
% nicht zuverlässig. Die eigentliche Zeichenlogik steckt deshalb in
% einer \cs{cs_new_protected:Npn}-Funktion, die als Ganzes durch alle
% Runden unangetastet durchgereicht wird (wie ein bereits
% \code{\\protected}-markierter Kernbefehl) und erst beim tatsächlichen
% Satz ausgeführt statt expandiert wird -- innerhalb dieser Funktion
% sind \cs{par} & Co. wieder ganz normal verwendbar, da dort keine
% weitere Expansion mehr stattfindet.}
% {Another @1 accessory: a thin, accent-colored line before and after
% the content instead of a full border (\code{rule-thickness = 0pt}
% forces this regardless of the instance's prior state) -- suits both
% quotations and theorem-like environments without their own box
% (\cls*{amsthm} does not frame its environments either). A \cs{traits}
% value passes through several full expansion rounds on its way from
% declaration to the finished instance (see
% \cs{__osgstyler_block_instance_resolve:n}); commands with their own
% lookahead for the next character (\cs{rule}'s optional raise
% argument, \cs{vspace}, \cs{newline}, \cs{par} inside a non-\cs{long}
% \pkg*{l3keys} argument) do not reliably survive that. The actual
% drawing logic therefore lives in a \cs{cs_new_protected:Npn} function
% that passes through all rounds untouched as a whole (like an
% already-\code{\\protected} kernel command) and is executed rather
% than expanded only at actual typesetting time -- inside that
% function, \cs{par} and friends are perfectly usable again, since no
% further expansion happens there.}
% {osl-modern}
\cs_new_protected:Npn \__osgstyler_osl_modern_rule:n #1
  {
    \noindent{\color{\OsgColorName{#1}}\vrule width\linewidth
      height.6pt depth0pt}
  }
\cs_new_protected:Npn \__osgstyler_osl_modern_rule_before:
  { \par \__osgstyler_osl_modern_rule:n {accent} \vskip.3em }
\cs_new_protected:Npn \__osgstyler_osl_modern_rule_after:
  { \vskip.3em \__osgstyler_osl_modern_rule:n {accent} }

\DeclareOsgBlockTrait { osl-modern-rules }
  {
    rule-thickness = 0pt,
    before = \__osgstyler_osl_modern_rule_before:,
    after  = \__osgstyler_osl_modern_rule_after:
  }

% \ldeen*{Gebrauchsfertige Block-Optiken als Traits. Farben sind
% Slot-Namen (\code{structure} usw.), sodass sie der ladenden Palette bzw.
% dem Theme folgen; \code{alertblock}/\code{exampleblock} überschreiben
% danach nur den Slot.
%
% \code{heading} (nur farbiger Titel), \code{soft} (Titel mit dünnen
% Linien) und \code{plain} fließen vollständig und belassen das Tagging der
% Klasse unangetastet -- die richtige Wahl, wenn jede \code{Div}-Ebene im
% getaggten PDF zählt. \code{card} (die Vorgabe), \code{titlebar} (Beamers
% Madrid: Farbbalken über getönter Body-Fläche), \code{framed} und
% \code{filled} setzen einen echten Kasten (\cs{fcolorbox} um eine
% \env{minipage}); das fügt je Block genau ein \code{Div} hinzu -- Titel
% und Body bleiben saubere \code{text-unit}s darin, ohne Verschachtelung.}
% {Ready-made block looks as traits. Colours are slot names
% (\code{structure} etc.) so they follow the loaded palette or theme;
% \code{alertblock}/\code{exampleblock} then only override the slot.
%
% \code{heading} (a coloured title only), \code{soft} (a title with thin
% rules) and \code{plain} are fully flowing and leave the class's tagging
% untouched -- the right choice when every \code{Div} level in tagged PDF
% counts. \code{card} (the default), \code{titlebar} (beamer's Madrid: a
% colour bar over a tinted body area), \code{framed} and \code{filled} put
% a real box (\cs{fcolorbox} around a \env{minipage}); that adds exactly
% one \code{Div} per block -- title and body stay clean \code{text-unit}s
% inside it, with no nesting.}
\DeclareOsgBlockTrait { plain }
  { layout = plain, rule-thickness = 0pt, background = none }
\DeclareOsgBlockTrait { framed }
  { layout = plain, rule-thickness = 0.4pt, rule-color = structure,
    padding = 0.5em }
\DeclareOsgBlockTrait { filled }
  { layout = plain, rule-thickness = 0.4pt, rule-color = structure,
    background = structure!8, padding = 0.5em }
% \ldeen{\code{titlebar}: Beamers Madrid -- voller Farbbalken oben, darunter
%   die getönte Body-Fläche bis zur Kastenunterkante. Durch das gesetzte
%   \code{body-background} nimmt der \code{titled}-Renderer seinen
%   Kastenzweig (ein \code{Div}).}{\code{titlebar}: beamer's Madrid -- a full
%   colour bar on top, the tinted body area below it down to the box's
%   bottom edge. The set \code{body-background} makes the \code{titled}
%   renderer take its boxed branch (one \code{Div}).}
\DeclareOsgBlockTrait { titlebar }
  {
    layout = titled,
    head-background = structure, head-color = background,
    body-background = structure!12,
    rule-thickness = 0pt, rule-color = structure!40,
    head-rule-thickness = 0pt, foot-rule-thickness = 0pt,
    head-padding = 0.4em, body-padding = 0.6em
  }
\DeclareOsgBlockTrait { soft }
  {
    layout = titled,
    head-background = none, head-color = structure,
    head-rule-color = structure, head-rule-thickness = 1pt,
    foot-rule-color = structure!45, foot-rule-thickness = 0.6pt,
    head-padding = 0pt, body-padding = 0.9em
  }
% \ldeen{\code{heading}: nur der farbige, fette Titel, darunter der bündige
%   Body -- fließend, ohne Balken, Fläche oder Linien. Wie Beamers
%   Default-Block (Hannover, Szeged). \code{head-rule}/\code{foot-rule} auf
%   einen positiven Wert setzen bringt die \code{soft}-Linien zurück.}{
%   \code{heading}: just the coloured bold title with the body flush below
%   it -- flowing, no bar, no fill, no rules. Like beamer's default block
%   (Hannover, Szeged). Setting \code{head-rule}/\code{foot-rule} to a
%   positive value brings the \code{soft} rules back.}
\DeclareOsgBlockTrait { heading }
  {
    layout = titled,
    head-background = none, head-color = structure,
    head-rule-color = structure, head-rule-thickness = 0pt,
    foot-rule-color = structure!45, foot-rule-thickness = 0pt,
    head-padding = 0pt, body-padding = 0pt
  }
% \ldeen{\code{card}: eine leicht gerahmte, zart getönte Box mit farbig
%   fettem Titel -- die Vorgabe. Als echter Kasten (\cs{fcolorbox} um eine
%   \env{minipage}) fügt sie im getaggten PDF ein \code{Div} je Block hinzu,
%   sonst nichts (Titel und Body bleiben saubere \code{text-unit}s darin).}{
%   \code{card}: a lightly framed, faintly tinted box with a coloured bold
%   title -- the default. Being a real box (\cs{fcolorbox} around a
%   \env{minipage}) it adds one \code{Div} per block in tagged PDF and
%   nothing else (title and body stay clean \code{text-unit}s inside it).}
\DeclareOsgBlockTrait { card }
  {
    layout = plain,
    rule-thickness = 0.8pt, rule-color = structure!70,
    background = structure!10,
    padding = 0.65em,
    head-color = structure, head-font = \normalfont \bfseries
  }

% \ldeen*{Ein Preset besteht aus bis zu vier unabhängig deklarierten
% Teilen (Farb-, Font- und Listenpalette, Block-Trait) unter demselben
% Namen; ohne @1 müsste eine Vorlage wie \code{osl-modern} durch vier
% getrennte Aufrufe aktiviert und der Trait zusätzlich von Hand auf
% jede gewünschte Block-Instanz angewendet werden. @1 aktiviert Farb-
% und (sofern @2 nicht \code{colors-only} gesetzt hat) Fontpalette
% immer; Listenpalette und Trait nur, wenn unter demselben Namen
% tatsächlich etwas deklariert ist -- @3 hat z.\,B.\ keine eigene
% Listenpalette. Der Trait wird auf die immer schon deklarierten
% Standard-Blockinstanzen (@4) angewendet, unabhängig davon, ob deren
% Environments überhaupt schon (via \cs{UseOsgStandardBlockEnvironments}
% oder \cs{BindOsgBlockEnvironment}) angebunden sind -- das Anwenden
% eines Traits ist reine, folgenlose Keyval-Verwaltung, bis eine
% tatsächlich gebundene Umgebung sie einmal abfragt.}
% {A preset consists of up to four independently declared parts (color,
% font, and list palette, block trait) sharing one name; without @1, a
% preset such as \code{osl-modern} would need four separate calls to
% activate, plus manually applying the trait to every desired block
% instance. @1 always activates the color palette and (unless @2 has
% set \code{colors-only}) the font palette; it activates the list
% palette and trait only if something is actually declared under the
% same name -- @3, for instance, has no list palette of its own. The
% trait is applied to the standard block instances (@4), which are
% always already declared, regardless of whether their environments
% are bound yet at all (via \cs{UseOsgStandardBlockEnvironments} or
% \cs{BindOsgBlockEnvironment}) -- applying a trait is pure,
% consequence-free keyval bookkeeping until some actually bound
% environment queries it.}
% {\cs{UseOsgPreset}}{osgstyler}{osl-standard}{\env{quote}/\env{quotation}/\env{block}/\env{alertblock}/\env{exampleblock}}
\msg_new:nnnn { osgstyler } { unknown-preset }
  { Unknown~preset~'#1'. }
  { Available~presets~are~osl-standard~and~osl-modern,~or~build~your~own~
    from~\token_to_str:N\UseOsgColorPalette,~\token_to_str:N\UseOsgFontPalette,~
    \token_to_str:N\UseOsgListPalette,~and~\token_to_str:N\StyleOsgBlockInstance. }

\clist_const:Nn \c__osgstyler_preset_block_instances_clist
  { quote, quotation, block, alertblock, exampleblock }

\cs_new_protected:Npn \__osgstyler_preset_use:n #1
  {
    \prop_if_in:NnTF \g__osgstyler_color_palette_prop { #1 / declared }
      {
        \UseOsgColorPalette {#1}
        \OsgFontPaletteAvailableTF
          {
            \prop_if_in:NnT \g__osgstyler_font_palette_prop { #1 / declared }
              { \UseOsgFontPalette {#1} }
          }
          { }
        \prop_if_in:NnT \g__osgstyler_list_palette_prop { #1 / declared }
          { \UseOsgListPalette {#1} }
        \prop_if_in:NnT \g__osgstyler_block_trait_prop { #1-rules }
          {
            \clist_map_inline:Nn \c__osgstyler_preset_block_instances_clist
              { \StyleOsgBlockInstance {##1} { traits = {#1-rules} } }
          }
      }
      { \msg_error:nnn { osgstyler } { unknown-preset } {#1} }
  }

\NewDocumentCommand \UseOsgPreset { m }
  { \__osgstyler_preset_use:n {#1} }

% ==========================================
% ATTRIBUTED ENVIRONMENTS (CASE B, CONTENT-THEN-ATTRIBUTION)
% ==========================================
%
% \ldeen*{Ein zweiter Fall-B-Baustein neben \cs{NewOsgStyledEnvironment}:
% ein semantisches Zitat trägt keine Titelzeile mit Label und Zahl vor
% dem Inhalt, sondern höchstens eine Quellenangabe danach -- die
% Zusammensetzung ist genau umgekehrt, weshalb dies ein eigener,
% schlanker Mechanismus ist statt einer weiteren Option von
% \cs{NewOsgStyledEnvironment}.}
% {A second case-B building block alongside \cs{NewOsgStyledEnvironment}:
% a semantic quotation carries no label-and-number title before its
% content, only an optional attribution after it -- the composition is
% exactly reversed, which is why this is its own, lean mechanism rather
% than another option of \cs{NewOsgStyledEnvironment}.}
% {\cs{NewOsgAttributedEnvironment}}

\tl_new:N \l__osgstyler_attributed_env_tl

\cs_new_protected:Npn \__osgstyler_attributed_env_begin:nnn #1#2#3
  {
    \group_begin:
      \tl_set:Nn \l__osgstyler_attributed_env_tl {#3}
      \IfNoValueF {#2}
        {
          \tl_put_right:Nn \l__osgstyler_attributed_env_tl
            { \par \hfill ---~#2 }
        }
      \__osgstyler_block_use:nnn {#1} { }
        { \tl_use:N \l__osgstyler_attributed_env_tl }
    \group_end:
  }

% See \cs{NewOsgStyledEnvironment} for why discarding the body
% conditionally is safe here.
\cs_new_protected:Npn \__osgstyler_attributed_env_mode_dispatch:nnnn #1#2#3#4
  {
    \IfNoValueTF {#2}
      { \__osgstyler_attributed_env_begin:nnn {#1}{#3}{#4} }
      {
        \IfLectureModeT {#2}
          { \__osgstyler_attributed_env_begin:nnn {#1}{#3}{#4} }
      }
  }

\NewDocumentCommand \NewOsgAttributedEnvironment { m m }
  {
    \cs_if_exist:NTF \IfLectureModeTF
      {
        \NewDocumentEnvironment {#1} { d<> o +b }
          { \__osgstyler_attributed_env_mode_dispatch:nnnn {#2}{##1}{##2}{##3} }
          { }
      }
      {
        \NewDocumentEnvironment {#1} { o +b }
          { \__osgstyler_attributed_env_begin:nnn {#2}{##1}{##2} }
          { }
      }
  }

% \ldeen*{Optionale Integration von Modi, falls @1 geladen wurde.
% Die Auswahlbefehle (\token_to_str:N\UseOsgInlineStyle und die gebundenen
% Inline-Befehle sowie \token_to_str:N\UseOsgColorPalette,
% \token_to_str:N\UseOsgFontPalette, \token_to_str:N\UseOsgMetricPalette)
% werden modusabhängig (Parameter in spitzen Klammern), da ihr aktiver Modus
% zum Zeitpunkt des Aufrufs bekannt ist. Die Deklarationsbefehle
% (\token_to_str:N\DeclareOsgColorPalette und Geschwister) bleiben bewußt
% moduunabhängig: Eine Palette wird einmalig definiert, unabhängig davon, in
% welchen Moden sie später ausgewählt wird.}{Optional osglecture-modes
% integration. The selection commands (\token_to_str:N\UseOsgInlineStyle and
% the bound inline commands, as well as \token_to_str:N\UseOsgColorPalette,
% \token_to_str:N\UseOsgFontPalette, \token_to_str:N\UseOsgMetricPalette)
% accept a portable angle-bracket mode whenever @1 is present, since their
% active mode is known at the call site. The declaration commands
% (\token_to_str:N\DeclareOsgColorPalette and siblings) deliberately remain
% mode-independent: a palette is defined once, regardless of which modes
% later select it.}{\pkg{osglecture-modes}}
\bool_new:N \g__osgstyler_modes_integrated_bool

\cs_new_protected:Npn \__osgstyler_modes_integrate:
  {
    \bool_if:NF \g__osgstyler_modes_integrated_bool
      {
        \bool_gset_true:N \g__osgstyler_modes_integrated_bool
        \__osgstyler_make_mode_aware:Nn \UseOsgInlineStyle { m +m }
        \prop_map_inline:Nn \g__osgstyler_inline_command_socket_prop
          {
            \exp_args:Nc \__osgstyler_make_mode_aware:Nn {##1} { +m }
          }
        \__osgstyler_make_mode_aware:Nn \UseOsgColorPalette { m }
        \__osgstyler_make_mode_aware:Nn \UseOsgFontPalette { m }
        \__osgstyler_make_mode_aware:Nn \UseOsgMetricPalette { m }
        \__osgstyler_make_mode_aware:Nn \UseOsgListPalette { m }
        \__osgstyler_make_mode_aware:Nn \UseOsgPreset { m }
      }
  }

\AddToHook { package/osglecture-modes/after } [ osgstyler/modes ]
  { \__osgstyler_modes_integrate: }
\IfPackageLoadedTF { osglecture-modes }
  { \__osgstyler_modes_integrate: } { }

% \ldeen*{Reaktive Rückfallintegration für Themepakete, die ihre
% Themefarben nicht selbst über @1/@2 in osgstyler einspeisen. Speist ein
% Themepaket seine Farben selbst ein und aktiviert dabei die
% osgstyler-Palette @3 (vgl.\ @4), erkennt dieser Abschnitt sich anhand
% der aktiven Palette selbst als überflüssig und tut nichts.}{Reactive
% fallback integration for theme packages that do not feed their theme
% colors into osgstyler themselves via @1/@2. A theme package that feeds
% its own colors in and activates the osgstyler palette @3 while doing so
% (cf.\ @4) makes this section recognize itself as redundant, via the
% active palette, and do nothing.}{\cs{DeclareOsgColorPalette}}
% {\cs{UseOsgColorPalette}}{\code{ltxtalk}}{\cs{setltxtalkcolors}}
\bool_new:N \g__osgstyler_lttheme_integrated_bool

\cs_new_protected:Npn \__osgstyler_lttheme_sync_colors:
  {
    \str_if_eq:VnF \g__osgstyler_color_palette_tl { ltxtalk }
      {
    \clist_map_inline:Nn \c__osgstyler_color_slots_clist
      {
        \str_if_eq:nnTF {##1} { background }
          { \tl_set:Nn \l_tmpa_tl { bg } }
          { \tl_set:Nn \l_tmpa_tl {##1} }
        \exp_args:Ne \color_export:nnN
          { ltxtalk-\l_tmpa_tl } { HTML } \l_tmpb_tl
        \prop_gput:Nnn \g__osgstyler_color_palette_prop
          { lttheme / ##1 / kind } { resource }
        \prop_gput:Nnn \g__osgstyler_color_palette_prop
          { lttheme / ##1 / model } { HTML }
        \prop_gput:Nne \g__osgstyler_color_palette_prop
          { lttheme / ##1 / value } { \l_tmpb_tl }
        \use:e
          {
            \exp_not:N \definecolor
              { osgstyler-##1 } { HTML } { \l_tmpb_tl }
          }
      }
    \prop_gput:Nnn \g__osgstyler_color_palette_prop
      { lttheme / declared } { true }
    \tl_gset:Nn \g__osgstyler_color_palette_tl { lttheme }
      }
  }

\cs_new_protected:Npn \__osgstyler_lttheme_integrate:
  {
    \bool_if:NF \g__osgstyler_lttheme_integrated_bool
      {
        \bool_gset_true:N \g__osgstyler_lttheme_integrated_bool
        \AddToHook { cmd/useltxtalktheme/after }
          [ osgstyler/lttheme/theme ]
          { \__osgstyler_lttheme_sync_colors: }
        \AddToHook { cmd/addltxtalktheme/after }
          [ osgstyler/lttheme/add-theme ]
          { \__osgstyler_lttheme_sync_colors: }
        \AddToHook { cmd/setltxtalkcolors/after }
          [ osgstyler/lttheme/colors ]
          { \__osgstyler_lttheme_sync_colors: }
        \__osgstyler_lttheme_sync_colors:
      }
  }

% \ldeen*{@1 ist ein Koordinationssignal: Ein Paket, das es vor dem
% eigenen @2 definiert (lttheme tut dies, siehe dort), zeigt damit an,
% dass es seine Themefarben selbst über @3/@4 einspeist und die reaktive
% Integration hier überflüssig, ja sogar riskant wäre. Riskant, weil @2
% aus Sicht von \LaTeX{} schon dann als geladen gilt, sobald das
% aufrufende Paket seine eigene \cs{ProvidesExplPackage}-Zeile
% durchlaufen hat -- lange bevor es seine eigenen Farben definiert; die
% reaktive Integration würde in diesem Moment sonst auf noch nicht
% existierende Farben zugreifen.}{@1 is a coordination signal: a package
% that defines it before its own @2 (lttheme does, see there) thereby
% indicates that it feeds its own theme colors into osgstyler via @3/@4
% itself, making the reactive integration here redundant, in fact risky.
% Risky, because @2 already counts as loaded from \LaTeX's perspective
% as soon as the requiring package's own \cs{ProvidesExplPackage} line
% has run -- long before it defines its own colors; the reactive
% integration would otherwise access not-yet-existing colors at that
% point.}
% {\cs{LtxtalkUsesOsgstylerNatively}}{\cs{RequirePackage}\Marg{osgstyler}}
% {\cs{DeclareOsgColorPalette}}{\cs{UseOsgColorPalette}}
\cs_if_exist:NF \LtxtalkUsesOsgstylerNatively
  {
    \AddToHook { package/ltxtalk-theme/after } [ osgstyler/lttheme ]
      { \__osgstyler_lttheme_integrate: }
    \IfPackageLoadedTF { ltxtalk-theme }
      { \__osgstyler_lttheme_integrate: } { }
  }

\ExplSyntaxOff



%</pkg>
%<*lua-backend>
\NeedsTeXFormat{LaTeX2e}[2024-06-01]
\ProvidesExplPackage{osgstyler-lua}
{2026-08-24}
{v0.2.0}
{Lua reference backend for osgstyler decorations}
\RequirePackage{osgstyler}
\RequirePackage{luacolor}
\RequirePackage{lua-ul}
\RequirePackage{pgfcore}
\RequirePackage{transparent}

\ExplSyntaxOn

\msg_new:nnnn { osgstyler-lua } { luatex-required }
  { The~osgstyler~Lua~backend~requires~LuaLaTeX. }
  { Use~LuaLaTeX~or~load~a~different~osgstyler~backend. }
\msg_new:nnnn { osgstyler-lua } { unsupported-pattern }
  { Pattern~'#1'~is~not~supported~for~the~'#2'~component. }
  { The~reference~backend~uses~a~solid~rule~instead. }
\msg_new:nnnn { osgstyler-lua } { unsupported-position }
  { Line~position~'#1'~is~not~supported. }
  { The~reference~backend~uses~the~'below'~position~instead. }
\msg_new:nnnn { osgstyler-lua } { unsupported-opacity }
  { Background~opacity~'#1'~is~outside~the~range~0--1. }
  { The~reference~backend~clamps~the~value~to~that~range. }
\msg_new:nnnn { osgstyler-lua } { unsupported-shape }
  { Background~shape~'#1'~is~not~supported. }
  { Use~'text'~or~'line';~the~reference~backend~falls~back~to~'text'. }
\msg_new:nnnn { osgstyler-lua } { unsupported-margin }
  { Margin~decoration~'#1'~cannot~be~rendered~inline. }
  { Use~a~block~backend~for~margin~decorations. }

\sys_if_engine_luatex:F
  { \msg_fatal:nn { osgstyler-lua } { luatex-required } }

% \RequirePackage{osgstyler} above already activates the built-in
% osgstyler-default metric palette unconditionally, so a metric
% palette is always active by this point; no separate fallback
% palette is needed here.

\tl_new:N \l__osgstyler_lua_pattern_tl
\tl_new:N \l__osgstyler_lua_position_tl
\tl_new:N \l__osgstyler_lua_opacity_tl
\tl_new:N \l__osgstyler_lua_shape_tl
\dim_new:N \l__osgstyler_lua_thickness_dim
\dim_new:N \l__osgstyler_lua_offset_dim
\dim_new:N \l__osgstyler_lua_top_dim
\dim_new:N \l__osgstyler_lua_bottom_dim
\dim_new:N \l__osgstyler_lua_cap_height_dim
\dim_new:N \l__osgstyler_lua_period_dim
\dim_new:N \l__osgstyler_lua_center_dim
\dim_new:N \l__osgstyler_lua_radius_dim
\dim_new:N \l__osgstyler_lua_padding_dim
\box_new:N \l__osgstyler_lua_content_box

% lua-ul deliberately accepts arbitrary leader material.  A context-dependent
% underline type lets it retain responsibility for finding decorated runs and
% splitting them at line boundaries, while pgfcore supplies the actual pattern.
\newunderlinetype \__osgstyler_lua_pattern_line:
  [
    \l__osgstyler_lua_pattern_tl @
    \dim_to_decimal_in_sp:n { \l__osgstyler_lua_top_dim } @
    \dim_to_decimal_in_sp:n { \l__osgstyler_lua_bottom_dim } @
    \dim_to_decimal_in_sp:n { \l__osgstyler_lua_thickness_dim } @
    \luaul_context_color:
  ]
  {
    \luaul_apply_color:
    \str_case:Vn \l__osgstyler_lua_pattern_tl
      {
        { dashed }
          {
            \dim_set:Nn \l__osgstyler_lua_period_dim
              { 6\l__osgstyler_lua_thickness_dim }
            \leaders\hbox_to_wd:nn { \l__osgstyler_lua_period_dim }
              {
                \vrule width 3\l__osgstyler_lua_thickness_dim
                  height \l__osgstyler_lua_top_dim
                  depth -\l__osgstyler_lua_bottom_dim
                \hss
              }
          }
        { dotted }
          {
            \dim_set:Nn \l__osgstyler_lua_period_dim
              { 3\l__osgstyler_lua_thickness_dim }
            \dim_set:Nn \l__osgstyler_lua_center_dim
              { (\l__osgstyler_lua_top_dim+\l__osgstyler_lua_bottom_dim) / 2 }
            \leaders\hbox_to_wd:nn { \l__osgstyler_lua_period_dim }
              {
                \begin{pgfpicture}
                  \pgfsetbaseline { 0pt }
                  \pgfsetfillcolor { \l__osgstyler_decoration_color_tl }
                  \pgfpathcircle
                    { \pgfpoint { .5\l__osgstyler_lua_thickness_dim }
                        { \l__osgstyler_lua_center_dim } }
                    { .5\l__osgstyler_lua_thickness_dim }
                  \pgfusepath { fill }
                \end{pgfpicture}
                \hss
              }
          }
        { wavy }
          {
            \dim_set:Nn \l__osgstyler_lua_period_dim
              { 6\l__osgstyler_lua_thickness_dim }
            \dim_set:Nn \l__osgstyler_lua_center_dim
              { (\l__osgstyler_lua_top_dim+\l__osgstyler_lua_bottom_dim) / 2 }
            \leaders\hbox_to_wd:nn { \l__osgstyler_lua_period_dim }
              {
                \begin{pgfpicture}
                  \pgfsetbaseline { 0pt }
                  \pgfsetstrokecolor { \l__osgstyler_decoration_color_tl }
                  \pgfsetlinewidth { \l__osgstyler_lua_thickness_dim }
                  \pgfpathmoveto
                    { \pgfpoint { 0pt } { \l__osgstyler_lua_center_dim } }
                  \pgfpathcurveto
                    { \pgfpoint { 1.5\l__osgstyler_lua_thickness_dim }
                        { \l__osgstyler_lua_top_dim } }
                    { \pgfpoint { 1.5\l__osgstyler_lua_thickness_dim }
                        { \l__osgstyler_lua_top_dim } }
                    { \pgfpoint { 3\l__osgstyler_lua_thickness_dim }
                        { \l__osgstyler_lua_center_dim } }
                  \pgfpathcurveto
                    { \pgfpoint { 4.5\l__osgstyler_lua_thickness_dim }
                        { \l__osgstyler_lua_bottom_dim } }
                    { \pgfpoint { 4.5\l__osgstyler_lua_thickness_dim }
                        { \l__osgstyler_lua_bottom_dim } }
                    { \pgfpoint { 6\l__osgstyler_lua_thickness_dim }
                        { \l__osgstyler_lua_center_dim } }
                  \pgfusepath { stroke }
                \end{pgfpicture}
                \hss
              }
          }
      }
  }

\newunderlinetype \__osgstyler_lua_background:
  [
    \l__osgstyler_lua_shape_tl @
    \l__osgstyler_lua_opacity_tl @
    \dim_to_decimal_in_sp:n { \l__osgstyler_lua_top_dim } @
    \dim_to_decimal_in_sp:n { \l__osgstyler_lua_bottom_dim } @
    \luaul_context_color:
  ]
  {
    \leaders\hbox_to_wd:nn { 1em }
      {
        \transparent { \l__osgstyler_lua_opacity_tl }
        \luaul_apply_color:
        \vrule width 1em
          height \l__osgstyler_lua_top_dim
          depth \l__osgstyler_lua_bottom_dim
        \hss
      }
  }

\cs_new_protected:Npn \__osgstyler_lua_pgf_pattern:n #1
  {
    \str_case:nnF {#1}
      {
        { solid } { \pgfsetdash { } { 0pt } }
        { dashed }
          { \pgfsetdash
              { { 3\l__osgstyler_lua_thickness_dim }
                { 2\l__osgstyler_lua_thickness_dim } } { 0pt } }
        { dotted }
          {
            \pgfsetroundcap
            \pgfsetdash
              { { 0pt } { 2.5\l__osgstyler_lua_thickness_dim } } { 0pt }
          }
      }
      {
        \msg_warning:nnnn { osgstyler-lua } { unsupported-pattern }
          {#1} { enclosure }
        \pgfsetdash { } { 0pt }
      }
  }

% Unlike the portable \fcolorbox fallback, this renderer owns the path and can
% therefore honour both corner radius and stroke pattern.  The content remains
% a normal tagged box while pgfcore emits the painted rectangle behind it.
\cs_new_protected:Npn \__osgstyler_lua_enclosure_apply:nnn #1#2#3
  {
    \group_begin:
      \__osgstyler_decoration_value:nnnnN {#1}{enclosure}{#2}{border-color}
        \l__osgstyler_decoration_color_tl
      \__osgstyler_decoration_default:Nn
        \l__osgstyler_decoration_color_tl { primary }
      \tl_set:Nx \l__osgstyler_decoration_color_tl
        { \OsgColorName { \l__osgstyler_decoration_color_tl } }
      \__osgstyler_decoration_value:nnnnN {#1}{enclosure}{#2}{fill-color}
        \l__osgstyler_decoration_value_tl
      \__osgstyler_decoration_default:Nn
        \l__osgstyler_decoration_value_tl { background }
      \tl_set:Nx \l_tmpa_tl
        { \OsgColorName { \l__osgstyler_decoration_value_tl } }
      \__osgstyler_decoration_value:nnnnN {#1}{enclosure}{#2}{pattern}
        \l__osgstyler_lua_pattern_tl
      \__osgstyler_decoration_default:Nn \l__osgstyler_lua_pattern_tl { solid }
      \__osgstyler_decoration_value:nnnnN {#1}{enclosure}{#2}{thickness}
        \l__osgstyler_decoration_value_tl
      \__osgstyler_decoration_default:Nn
        \l__osgstyler_decoration_value_tl { rule/thin }
      \exp_args:NV \__osgstyler_decoration_metric_resolve:nN
        \l__osgstyler_decoration_value_tl \l__osgstyler_decoration_value_tl
      \dim_set:Nn \l__osgstyler_lua_thickness_dim
        { \l__osgstyler_decoration_value_tl }
      \__osgstyler_decoration_value:nnnnN {#1}{enclosure}{#2}{padding}
        \l__osgstyler_decoration_value_tl
      \__osgstyler_decoration_default:Nn
        \l__osgstyler_decoration_value_tl { padding/normal }
      \exp_args:NV \__osgstyler_decoration_metric_resolve:nN
        \l__osgstyler_decoration_value_tl \l__osgstyler_decoration_value_tl
      \dim_set:Nn \l__osgstyler_lua_padding_dim
        { \l__osgstyler_decoration_value_tl + .5\l__osgstyler_lua_thickness_dim }
      \__osgstyler_decoration_value:nnnnN {#1}{enclosure}{#2}{radius}
        \l__osgstyler_decoration_value_tl
      \__osgstyler_decoration_default:Nn
        \l__osgstyler_decoration_value_tl { radius/square }
      \exp_args:NV \__osgstyler_decoration_metric_resolve:nN
        \l__osgstyler_decoration_value_tl \l__osgstyler_decoration_value_tl
      \dim_set:Nn \l__osgstyler_lua_radius_dim
        { \l__osgstyler_decoration_value_tl }
      \hbox_set:Nn \l__osgstyler_lua_content_box
        { \kern\l__osgstyler_lua_padding_dim #3
          \kern\l__osgstyler_lua_padding_dim }
      \box_set_ht:Nn \l__osgstyler_lua_content_box
        { \box_ht:N \l__osgstyler_lua_content_box
          + \l__osgstyler_lua_padding_dim }
      \box_set_dp:Nn \l__osgstyler_lua_content_box
        { \box_dp:N \l__osgstyler_lua_content_box
          + \l__osgstyler_lua_padding_dim }
      \hbox:n
        {
          \rlap
            {
                \begin{pgfpicture}
                  \pgfsetbaseline { 0pt }
                  \pgfsetfillcolor { \l_tmpa_tl }
                  \pgfsetstrokecolor { \l__osgstyler_decoration_color_tl }
                  \pgfsetlinewidth { \l__osgstyler_lua_thickness_dim }
                  \exp_args:NV \__osgstyler_lua_pgf_pattern:n
                    \l__osgstyler_lua_pattern_tl
                  \pgfsetcornersarced
                    { \pgfpoint { \l__osgstyler_lua_radius_dim }
                        { \l__osgstyler_lua_radius_dim } }
                  \pgfpathrectanglecorners
                    { \pgfpoint
                        { \dim_eval:n { .5\l__osgstyler_lua_thickness_dim } }
                        { \dim_eval:n
                            { -\box_dp:N \l__osgstyler_lua_content_box
                              + .5\l__osgstyler_lua_thickness_dim } } }
                    { \pgfpoint
                        { \dim_eval:n
                            { \box_wd:N \l__osgstyler_lua_content_box
                              - .5\l__osgstyler_lua_thickness_dim } }
                        { \dim_eval:n
                            { \box_ht:N \l__osgstyler_lua_content_box
                              - .5\l__osgstyler_lua_thickness_dim } } }
                  \dim_compare:nNnTF
                    \l__osgstyler_lua_thickness_dim > \c_zero_dim
                    { \pgfusepath { fill, stroke } }
                    { \pgfusepath { fill } }
                \end{pgfpicture}
            }
          \box_use_drop:N \l__osgstyler_lua_content_box
        }
    \group_end:
  }

\cs_new_protected:Npn \__osgstyler_lua_line_apply:nnn #1#2#3
  {
    \group_begin:
      \__osgstyler_decoration_value:nnnnN {#1}{line}{#2}{position}
        \l__osgstyler_lua_position_tl
      \__osgstyler_decoration_default:Nn \l__osgstyler_lua_position_tl { below }
      \__osgstyler_decoration_value:nnnnN {#1}{line}{#2}{pattern}
        \l__osgstyler_lua_pattern_tl
      \__osgstyler_decoration_default:Nn \l__osgstyler_lua_pattern_tl { solid }
      \clist_if_in:nVF { solid,dashed,dotted,wavy }
        \l__osgstyler_lua_pattern_tl
        {
          \msg_warning:nnee { osgstyler-lua } { unsupported-pattern }
            \l__osgstyler_lua_pattern_tl \l__osgstyler_lua_position_tl
          \tl_set:Nn \l__osgstyler_lua_pattern_tl { solid }
        }
      \__osgstyler_decoration_value:nnnnN {#1}{line}{#2}{color}
        \l__osgstyler_decoration_color_tl
      \__osgstyler_decoration_default:Nn \l__osgstyler_decoration_color_tl { text }
      \tl_set:Nx \l__osgstyler_decoration_color_tl
        { \OsgColorName { \l__osgstyler_decoration_color_tl } }
      \__osgstyler_decoration_value:nnnnN {#1}{line}{#2}{thickness}
        \l__osgstyler_decoration_value_tl
      \__osgstyler_decoration_default:Nn \l__osgstyler_decoration_value_tl { rule/thin }
      \exp_args:NV \__osgstyler_decoration_metric_resolve:nN
        \l__osgstyler_decoration_value_tl \l__osgstyler_decoration_value_tl
      \dim_set:Nn \l__osgstyler_lua_thickness_dim
        { \l__osgstyler_decoration_value_tl }
      \__osgstyler_decoration_value:nnnnN {#1}{line}{#2}{offset}
        \l__osgstyler_decoration_value_tl
      \__osgstyler_decoration_default:Nn \l__osgstyler_decoration_value_tl { .1ex }
      \exp_args:NV \__osgstyler_decoration_metric_resolve:nN
        \l__osgstyler_decoration_value_tl \l__osgstyler_decoration_value_tl
      \dim_set:Nn \l__osgstyler_lua_offset_dim
        { \l__osgstyler_decoration_value_tl }
      \str_case:VnF \l__osgstyler_lua_position_tl
        {
          { below }
            {
              \dim_set:Nn \l__osgstyler_lua_top_dim
                { -\l__osgstyler_lua_offset_dim }
              \dim_set:Nn \l__osgstyler_lua_bottom_dim
                { -\l__osgstyler_lua_offset_dim
                  -\l__osgstyler_lua_thickness_dim }
            }
          { above }
            {
              \dim_set:Nn \l__osgstyler_lua_cap_height_dim
                { \fontcharht\font `X }
              \dim_set:Nn \l__osgstyler_lua_top_dim
                { \l__osgstyler_lua_cap_height_dim
                  +\l__osgstyler_lua_offset_dim
                  +\l__osgstyler_lua_thickness_dim }
              \dim_set:Nn \l__osgstyler_lua_bottom_dim
                { \l__osgstyler_lua_cap_height_dim
                  +\l__osgstyler_lua_offset_dim }
            }
          { through }
            {
              \dim_set:Nn \l__osgstyler_lua_top_dim
                { \l__osgstyler_lua_offset_dim
                  +.5\l__osgstyler_lua_thickness_dim }
              \dim_set:Nn \l__osgstyler_lua_bottom_dim
                { \l__osgstyler_lua_offset_dim
                  -.5\l__osgstyler_lua_thickness_dim }
            }
        }
        {
          \msg_warning:nne { osgstyler-lua } { unsupported-position }
            \l__osgstyler_lua_position_tl
          \dim_set:Nn \l__osgstyler_lua_top_dim
            { -\l__osgstyler_lua_offset_dim }
          \dim_set:Nn \l__osgstyler_lua_bottom_dim
            { -\l__osgstyler_lua_offset_dim
              -\l__osgstyler_lua_thickness_dim }
        }
      \str_if_eq:VnTF \l__osgstyler_lua_pattern_tl { solid }
        {
          \underLine
            [ color=\l__osgstyler_decoration_color_tl,
              top=\l__osgstyler_lua_top_dim,
              bottom=\l__osgstyler_lua_bottom_dim ]
            {#3}
        }
        {
          \luaul_set_color:n { \l__osgstyler_decoration_color_tl }
          \__osgstyler_lua_pattern_line: #3
        }
    \group_end:
  }

\cs_new_protected:Npn \__osgstyler_lua_background_apply:nnn #1#2#3
  {
    \group_begin:
      \__osgstyler_decoration_value:nnnnN {#1}{background}{#2}{color}
        \l__osgstyler_decoration_color_tl
      \__osgstyler_decoration_default:Nn \l__osgstyler_decoration_color_tl { accent }
      \tl_set:Nx \l__osgstyler_decoration_color_tl
        { \OsgColorName { \l__osgstyler_decoration_color_tl } }
      \__osgstyler_decoration_value:nnnnN {#1}{background}{#2}{opacity}
        \l__osgstyler_lua_opacity_tl
      \__osgstyler_decoration_default:Nn \l__osgstyler_lua_opacity_tl { 1 }
      \fp_compare:nNnTF { \l__osgstyler_lua_opacity_tl } < { 0 }
        {
          \msg_warning:nne { osgstyler-lua } { unsupported-opacity }
            \l__osgstyler_lua_opacity_tl
          \tl_set:Nn \l__osgstyler_lua_opacity_tl { 0 }
        }
        {
          \fp_compare:nNnT { \l__osgstyler_lua_opacity_tl } > { 1 }
            {
              \msg_warning:nne { osgstyler-lua } { unsupported-opacity }
                \l__osgstyler_lua_opacity_tl
              \tl_set:Nn \l__osgstyler_lua_opacity_tl { 1 }
            }
        }
      \__osgstyler_decoration_value:nnnnN {#1}{background}{#2}{shape}
        \l__osgstyler_lua_shape_tl
      \__osgstyler_decoration_default:Nn \l__osgstyler_lua_shape_tl { text }
      \str_case:VnF \l__osgstyler_lua_shape_tl
        {
          { text }
            {
              \dim_set:Nn \l__osgstyler_lua_top_dim { 1.75ex }
              \dim_set:Nn \l__osgstyler_lua_bottom_dim { .75ex }
            }
          { line }
            {
              \dim_set:Nn \l__osgstyler_lua_top_dim { .8\baselineskip }
              \dim_set:Nn \l__osgstyler_lua_bottom_dim { .2\baselineskip }
            }
        }
        {
          \msg_warning:nne { osgstyler-lua } { unsupported-shape }
            \l__osgstyler_lua_shape_tl
          \tl_set:Nn \l__osgstyler_lua_shape_tl { text }
          \dim_set:Nn \l__osgstyler_lua_top_dim { 1.75ex }
          \dim_set:Nn \l__osgstyler_lua_bottom_dim { .75ex }
        }
      \luaul_set_color:n { \l__osgstyler_decoration_color_tl }
      \__osgstyler_lua_background: #3
    \group_end:
  }

\cs_new_protected:Npn \__osgstyler_lua_family_wrap:nnN #1#2#3
  {
    \tl_set:Nx \l__osgstyler_decoration_instances_tl
      { \OsgDecorationInstances {#1}{#2} }
    \clist_map_inline:Vn \l__osgstyler_decoration_instances_tl
      {
        \tl_set:Nx #3
          {
            \exp_not:c { __osgstyler_lua_#2_apply:nnn }
              { \exp_not:n {#1} } { \exp_not:n {##1} }
              { \exp_not:V #3 }
          }
      }
  }

\cs_new_protected:Npn \__osgstyler_lua_decoration_processor:nn #1#2
  {
    \group_begin:
      \__osgstyler_decoration_affixes_apply:nn {#1}{#2}
      \__osgstyler_lua_family_wrap:nnN {#1}{line}
        \l__osgstyler_decoration_content_tl
      \__osgstyler_lua_family_wrap:nnN {#1}{background}
        \l__osgstyler_decoration_content_tl
      \__osgstyler_lua_family_wrap:nnN {#1}{enclosure}
        \l__osgstyler_decoration_content_tl
      \OsgDecorationIfHasComponentTF {#1}{margin}
        { \msg_warning:nnn { osgstyler-lua } { unsupported-margin } {#1} }
        { }
      \tl_use:N \l__osgstyler_decoration_content_tl
    \group_end:
  }

\cs_new_eq:NN \OsgStylerLuaDecorationProcessor
  \__osgstyler_lua_decoration_processor:nn
\SetOsgDecorationProcessor { \OsgStylerLuaDecorationProcessor }
\bool_gset_true:N \g__osgstyler_decoration_backend_bool

\NewExpandableDocumentCommand \OsgStylerLuaBackendActive { } { true }

\ExplSyntaxOff
%</lua-backend>
