% \iffalse meta-comment
% Copyright (C) 2026 Andreas Willibald Weber
%
% This work may be distributed and/or modified under the conditions of the
% LaTeX Project Public License, either version 1.3c of this license or (at your
% option) any later version. The latest version of the license is available at
% https://www.latex-project.org/lppl.txt.
%
% This work has the LPPL maintenance status `maintained'. The Current
% Maintainer of this work is Andreas Willibald Weber.
%
% This file is part of the `kanban-board' package. The complete list of files
% constituting the work appears in the main source file kanban-board.dtx.
% \fi
%
%    \begin{macrocode}
%<*package>
%    \end{macrocode}
%
% \subsection{Card components and renderers}
%
% Metadata elements are small inline boxes. Their semantic colors are stable
% across modes. Only the card surface, card text, and card border respond to
% the light or dark board mode. Public namespaced styles are appended after
% the structural defaults. Fixed and breakable layouts reuse the same card
% renderer but provide different column containers. Rendering is delayed.
% Task declarations store calls to these components, and the environment
% executes those calls only after it knows all column counts and dimensions.
%
% \paragraph{Structural and extension styles.}
% The style registry distinguishes fixed package structure from supported
% user extension points. The empty public styles may be extended with
% |\tcbset|. The private styles establish the common geometry of inline pills
% and the stable author and deadline colors. Because the public extension key
% is applied last in each component definition, a document may override
% presentation without replacing structural collection logic.
%    \begin{macrocode}
\tcbset{
  kanban/header/.style={},
  kanban/card/.style={},
  kanban/column-body/.style={},
  kanban/priority-chip/.style={},
  kanban/author-chip/.style={},
  kanban/deadline-chip/.style={},
  kanban/completed-chip/.style={},
  kanban@pill/.style={on line,boxsep=0pt,left=1.2mm,right=1.2mm,
    top=.4mm,bottom=.4mm,arc=.7mm,boxrule=0pt,nobeforeafter,
    box align=base},
  kanban@chip/.style={kanban@pill,colback=\kanban@authorback,
    coltext=\kanban@authortext},
  kanban@due/.style={kanban@pill,colback=\kanban@deadlineback,
    coltext=\kanban@deadlinetext}}
%    \end{macrocode}
%
% \paragraph{Priority and author chips.}
% A priority chip maps a normalized semantic value to the board-local label,
% the semantic background, and the matching foreground. An empty or unknown
% value produces no chip. The author component has no semantic
% classification and renders any non-blank author text with the stable
% author style.
%    \begin{macrocode}
\newcommand\kanban@prioritypill[1]{\ifblank{#1}{}{%
  \def\kb@pl{}\def\kb@pc{}\def\kb@pt{}%
  \ifstrequal{#1}{high}{\def\kb@pl{\kanban@boardhighlabel}%
    \def\kb@pc{kanbanHigh}\def\kb@pt{\kanban@hightext}}{}%
  \ifstrequal{#1}{medium}{\def\kb@pl{\kanban@boardmediumlabel}%
    \def\kb@pc{kanbanMed}\def\kb@pt{\kanban@mediumtext}}{}%
  \ifstrequal{#1}{med}{\def\kb@pl{\kanban@boardmediumlabel}%
    \def\kb@pc{kanbanMed}\def\kb@pt{\kanban@mediumtext}}{}%
  \ifstrequal{#1}{low}{\def\kb@pl{\kanban@boardlowlabel}%
    \def\kb@pc{kanbanLow}\def\kb@pt{\kanban@lowtext}}{}%
  \ifdefempty\kb@pl{}{\tcbox[kanban@pill,
    colback=\kb@pc,colframe=\kb@pc,coltext=\kb@pt,
    kanban/priority-chip]{\bfseries\scriptsize\kb@pl}}}}
\newcommand\kanban@authorchip[1]{\ifblank{#1}{}{%
  \tcbox[kanban@chip,kanban/author-chip]{\scriptsize #1}}}
%    \end{macrocode}
%
% \paragraph{The deadline clock symbol.}
% Deadline chips include a compact clock drawn with TikZ primitives, so the
% package does not depend on an icon font. Its dimensions are fixed in points
% and small enough to align with script-sized metadata text. The picture is
% self-contained and does not alter later TikZ material.
%    \begin{macrocode}
\newcommand\kb@clock{\tikz[baseline=-.55ex]{%
  \draw[line width=.3pt] (0,0) circle (1.7pt);
  \draw[line width=.3pt] (0,0)--(0,1.1pt);
  \draw[line width=.3pt] (0,0)--(1pt,.2pt);}}
%    \end{macrocode}
%
% \paragraph{Deadline and completion chips.}
% Deadline rendering first classifies the value and then prepares its
% display notation. A task marked |late| or an automatically overdue date
% receives the high semantic color. A date equal to the reference date
% receives the medium color. Future dates, project labels, and descriptive
% text keep the neutral deadline style. Completion is a separate flag
% rendered with the low semantic color. These colors are identical in light
% and dark board modes.
%    \begin{macrocode}
\newcommand\kanban@deadlinechip[2]{\ifblank{#2}{}{%
  \kb@evaloverdue{#2}%
  \kanban@preparedeadline{#2}%
  \ifboolexpr{test {\ifstrequal{#1}{1}} or bool {kb@overdue}}
    {\tcbox[kanban@due,colback=kanbanHigh,coltext=\kanban@hightext,
        kanban/deadline-chip]
      {\kb@clock\,\scriptsize\bfseries \kb@displaydeadline}}
    {\ifbool{kb@duenow}
      {\tcbox[kanban@due,colback=kanbanMed,coltext=\kanban@mediumtext,
          kanban/deadline-chip]
        {\kb@clock\,\scriptsize\bfseries \kb@displaydeadline}}
      {\tcbox[kanban@due,kanban/deadline-chip]
        {\kb@clock\,\scriptsize \kb@displaydeadline}}}}}
\newcommand\kanban@completedchip[1]{\ifstrequal{#1}{1}
  {\tcbox[kanban@pill,colback=kanbanLow,colframe=kanbanLow,
      coltext=\kanban@lowtext,kanban/completed-chip]
    {\bfseries\scriptsize\kanban@boarddonelabel}}{}}
%    \end{macrocode}
%
% \paragraph{Priority-value validation.}
% Task options must store only priority values understood by the renderer.
% The setter normalizes case and surrounding spaces, accepts the four
% documented spellings, treats a blank choice as omission, and warns when
% non-blank input is unknown. The normalized value is stored with the task.
% All definitions occur while a task is collected inside the environment
% group, so the temporary value cannot leak to later tasks. The warning is
% a named message, so the definition sits in an expl3 syntax region.
%
% The background setter validates the optional card tint before TeX reaches
% \textsf{xcolor}, because an unknown color would otherwise stop the run
% while the board is rendered, far from the declaration that caused it. The
% accepted grammar is a color name, optionally negated, followed by any
% number of |!|\meta{percentage}|!|\meta{name} mixing steps. Every name in
% the expression must be known to \textsf{xcolor}. The value is expanded
% with |\text_expand:n| first, so an ordinary macro that holds a color name
% works and a robust command is rejected as unknown.
%    \begin{macrocode}
\ExplSyntaxOn
\cs_new_protected:Npn \kanban@setbackground #1
  {
    \tl_set:Nx \l__kanban_background_tl {\text_expand:n {#1}}
    \tl_trim_spaces:N \l__kanban_background_tl
    \cs_set:Npn \kbtask@bg {}
    \tl_if_blank:VF \l__kanban_background_tl
      {
        \bool_set_false:N \l__kanban_background_ok_bool
        \exp_args:NnV \regex_match:nnT
          {
            \A-?[A-Za-z][A-Za-z0-9]*
            (?:![0-9]{1,3}(?:![A-Za-z][A-Za-z0-9]*)?)*\Z
          }
          \l__kanban_background_tl
          {
            \bool_set_true:N \l__kanban_background_ok_bool
            \exp_args:NnV \regex_extract_all:nnN {[A-Za-z][A-Za-z0-9]*}
              \l__kanban_background_tl \l__kanban_background_seq
            \seq_map_inline:Nn \l__kanban_background_seq
              {
                \@ifundefinedcolor{##1}
                  {\bool_set_false:N \l__kanban_background_ok_bool}
                  {}
              }
          }
        \bool_if:NTF \l__kanban_background_ok_bool
          {\cs_set:Npx \kbtask@bg {\tl_use:N \l__kanban_background_tl}}
          {
            \use:x
              {
                \msg_warning:nnn {kanban-board}{unknown-color}
                  {\tl_to_str:N \l__kanban_background_tl}
              }
          }
      }
  }
\newcommand\kanban@setpriority[1]{%
  \kanban@normalizechoice{#1}%
  \def\kbtask@prio{}%
  \ifdefstring{\kb@choice}{high}{\def\kbtask@prio{high}}{%
    \ifdefstring{\kb@choice}{medium}{\def\kbtask@prio{medium}}{%
      \ifdefstring{\kb@choice}{med}{\def\kbtask@prio{med}}{%
        \ifdefstring{\kb@choice}{low}{\def\kbtask@prio{low}}{%
          \ifdefempty\kb@choice{}
            {\msg_warning:nnn {kanban-board}{unknown-priority}{#1}}}}}}}
\ExplSyntaxOff
%    \end{macrocode}
%
% \paragraph{Break-layout headers and shared task cards.}
% The page-breakable renderer needs a header box whose height is
% synchronized across its independent columns through a board-specific
% equal-height group. All renderers need one full-width card whose surface
% and text follow the active mode and whose colored west rule identifies the
% target column. The card surface is either the plain surface of the mode or
% that surface mixed with a tint. The tint is a stored \textsf{xcolor}
% expression, so it is first bound to a named color and then mixed with the
% mode percentage, which keeps arbitrary expressions valid as a mixing
% operand. The shared card composer resolves the surface, places the task
% text first, then an optional row with priority, completion, and author,
% and finally an optional deadline row. Storing the same composer for both
% layouts guarantees identical card semantics.
%    \begin{macrocode}
\newtcolorbox{kanban@header}[2]{enhanced,sharp corners,colback=#1,
  boxrule=0pt,coltext=#2,
  fontupper=\bfseries\raggedright,
  left=2mm,right=2mm,
  top=1.2mm,bottom=1.2mm,before skip=0pt,after skip=1.8mm,
  equal height group={KBH@\kanban@boardgroup},valign=center,
  kanban/header}
\newtcolorbox{kanban@card}[2]{enhanced,arc=.6mm,outer arc=.6mm,
  colback=#2,colframe=\kanban@bordercolor,
  coltext=\kanban@textcolor,boxrule=.4pt,width=\linewidth,
  borderline west={2.2pt}{0pt}{#1},left=2.4mm,right=2mm,top=1.6mm,
  bottom=1.6mm,before skip=0pt,after skip=1.8mm,
  fontupper=\scriptsize\raggedright,kanban/card}
\newcommand\kanban@resolvecardback[1]{%
  \ifblank{#1}
    {\let\kb@cardback\kanban@cardcolor}
    {\colorlet{kb@tintbase}{#1}%
     \edef\kb@cardback{kb@tintbase!\kanban@cardtint!\kanban@cardcolor}}}
\newcommand\kanban@rendercard[8]{%
  \kanban@resolvecardback{#8}%
  \begin{kanban@card}{#1}{\kb@cardback}#2%
    \notblank{#3#4#7}{\par\vspace{1mm}\noindent
      \kanban@prioritypill{#3}%
      \notblank{#3}{\notblank{#7}{\hspace{1mm}}{}}{}%
      \kanban@completedchip{#7}\hfill\kanban@authorchip{#4}}{}%
    \notblank{#5}{\par\vspace{1mm}\noindent
      \kanban@deadlinechip{#6}{#5}}{}%
  \end{kanban@card}}
%    \end{macrocode}
%
% \paragraph{Fixed-board containers and renderer adapters.}
% The fixed renderer uses a two-row raster. The first row contains
% synchronized headings and the second contains equal-height column bodies.
% The body hides both frame and interior, so the page shows through without
% a transparency group in the PDF, and draws only a bottom rule in a tint of
% the column color. The breakable renderer instead places each header and
% task list in a separate |paracol| stream. Three adapters insert the stored
% column name, count, color, foreground, and deferred card list into the
% appropriate container.
%    \begin{macrocode}
\newtcolorbox{kanban@fixedheader}[2]{enhanced,sharp corners,
  colback=#1,boxrule=0pt,coltext=#2,
  fontupper=\bfseries\raggedright,boxsep=0pt,left=2mm,right=2mm,
  top=1mm,bottom=1mm,before skip=0pt,after skip=0pt,valign=center,
  kanban/header}
\newtcolorbox{kanban@fixedbody}[1]{enhanced,sharp corners,frame hidden,
  interior hidden,boxrule=0pt,
  borderline south={.6pt}{0pt}{#1!55},boxsep=0pt,
  left=0pt,right=0pt,top=2mm,bottom=2mm,
  before skip=0pt,after skip=0pt,valign=top,kanban/column-body}
\newcommand\kanban@onehead[1]{%
  \begin{kanban@fixedheader}{\csuse{kanban@color@#1}}%
      {\csuse{kanban@headingtext@#1}}%
    \csuse{kanban@name@#1}\hfill
      \csuse{kanban@count@\kanban@boardgroup @#1}%
  \end{kanban@fixedheader}}
\newcommand\kanban@onecol[1]{%
  \begin{kanban@fixedbody}{\csuse{kanban@color@#1}}%
    \csuse{kanban@list@\kanban@boardgroup @#1}%
  \end{kanban@fixedbody}}
\newcommand\kanban@onecol@b[1]{%
  \begin{kanban@header}{\csuse{kanban@color@#1}}%
      {\csuse{kanban@headingtext@#1}}%
    \csuse{kanban@name@#1}\hfill
      \csuse{kanban@count@\kanban@boardgroup @#1}%
  \end{kanban@header}%
  \csuse{kanban@list@\kanban@boardgroup @#1}\par}
%    \end{macrocode}
%
%    \begin{macrocode}
%</package>
%    \end{macrocode}
