% Author and maintainer: SuFan (GitHub: Tape5try)
% Contact: 3546236610@qq.com
% Copyright (c) 2026 SuFan. MIT License; see LICENSE.
\NeedsTeXFormat{LaTeX2e}[2020/10/01]
\ProvidesPackage{gradbars}[2026/10/06 v0.0.4 Inline data bars for LaTeX]
\RequirePackage{tikz}
\RequirePackage{xparse}
\RequirePackage{expl3}
\RequirePackage{collcell}
\RequirePackage{booktabs}
\RequirePackage{colortbl}
\RequirePackage{longtable}
\usetikzlibrary{shadings,patterns}

\definecolor{gradbarsBlue}{HTML}{2563EB}
\definecolor{gradbarsBlueLight}{HTML}{93C5FD}
\definecolor{gradbarsTeal}{HTML}{0F766E}
\definecolor{gradbarsTealLight}{HTML}{5EEAD4}
\definecolor{gradbarsInk}{HTML}{243247}
\definecolor{gradbarsTrack}{HTML}{EDF2F7}
% Public built-in palette.
\definecolor{lightgreen}{RGB}{144,238,144}
\definecolor{lightyellow}{RGB}{255,255,224}
\definecolor{lightblue}{RGB}{173,216,230}
\definecolor{lightred}{RGB}{255,160,122}
\definecolor{rose}{RGB}{255,102,204}
\definecolor{skyblue}{RGB}{135,206,235}
\definecolor{gold}{RGB}{255,215,0}
\definecolor{lavender}{RGB}{230,230,250}
\definecolor{peach}{RGB}{255,218,185}
\newif\ifgradbars@valid
\newif\ifgradbars@gradient
\def\gradbars@width{24mm}
\def\gradbars@height{1.5ex}
\def\gradbars@max{100}
\def\gradbars@min{0}
\def\gradbars@rounded{0pt}
\def\gradbars@target{}
\def\gradbars@targetcolor{gradbarsInk}
\def\gradbars@negativecolor{lightred}
\def\gradbars@thresholds{}
\def\gradbars@thresholdcolors{lightred,gold,lightgreen}
\def\gradbars@errorminus{}
\def\gradbars@errorplus{}
\def\gradbars@errorcolor{gradbarsInk}
\newif\ifgradbars@error
\newsavebox\gradbars@labelbox
\def\gradbars@precision{1}
\def\gradbars@unit{}
\def\gradbars@text{}
\def\gradbars@alt{}
\def\gradbars@accessibility{auto}
\newif\ifgradbars@capturealt
\def\gradbars@label{outside}
\def\gradbars@format{value}
\def\gradbars@overflow{clip}
\def\gradbars@gap{.6em}
\def\gradbars@labelwidth{4.5em}
\def\gradbars@font{\relax}
\newif\ifgradbars@missing
\newif\ifgradbars@stack
\newcount\gradbars@styledepth
\def\gradbars@missingtext{--}
\def\gradbars@numberformat{fixed}
\def\gradbars@band{}
\def\gradbars@bandcolor{gold}
\def\gradbars@bandopacity{0.3}
\def\gradbars@stackcolors{gradbarsBlue,gradbarsTeal,gold}
\pgfkeys{/gradbars/.is family,/gradbars,
  style/.code={\gradbarsUseStyle{#1}},
  missing text/.store in=\gradbars@missingtext,
  number format/.is choice,
  number format/fixed/.code=\def\gradbars@numberformat{fixed},
  number format/grouped/.code=\def\gradbars@numberformat{grouped},
  number format/scientific/.code=\def\gradbars@numberformat{scientific},
  band/.store in=\gradbars@band,
  band color/.store in=\gradbars@bandcolor,
  band opacity/.store in=\gradbars@bandopacity,
  stack colors/.store in=\gradbars@stackcolors,
  width/.store in=\gradbars@width,
  height/.store in=\gradbars@height,
  max/.store in=\gradbars@max,
  min/.store in=\gradbars@min,
  rounded/.store in=\gradbars@rounded,
  target/.store in=\gradbars@target,
  target color/.store in=\gradbars@targetcolor,
  negative color/.store in=\gradbars@negativecolor,
  thresholds/.store in=\gradbars@thresholds,
  threshold colors/.store in=\gradbars@thresholdcolors,
  error/.style={error minus=#1,error plus=#1},
  error minus/.store in=\gradbars@errorminus,
  error plus/.store in=\gradbars@errorplus,
  error color/.store in=\gradbars@errorcolor,
  precision/.store in=\gradbars@precision,
  unit/.store in=\gradbars@unit,
  text/.store in=\gradbars@text,
  alt/.store in=\gradbars@alt,
  accessibility/.is choice,
  accessibility/auto/.code=\def\gradbars@accessibility{auto},
  accessibility/artifact/.code=\def\gradbars@accessibility{artifact},
  accessibility/off/.code=\def\gradbars@accessibility{off},
  label gap/.store in=\gradbars@gap,
  label width/.store in=\gradbars@labelwidth,
  font/.store in=\gradbars@font,
  left color/.store in=\gradbars@left,
  right color/.store in=\gradbars@right,
  track color/.store in=\gradbars@track,
  text color/.store in=\gradbars@textcolor,
  color/.style={fill=solid,left color=#1},
  fill/.is choice,
  fill/gradient/.code=\gradbars@gradienttrue,
  fill/solid/.code=\gradbars@gradientfalse,
  label/.is choice,
  label/outside/.code=\def\gradbars@label{outside},
  label/inside/.code=\def\gradbars@label{inside},
  label/none/.code=\def\gradbars@label{none},
  label/end/.code=\def\gradbars@label{end},
  label/auto/.code=\def\gradbars@label{auto},
  value format/.is choice,
  value format/value/.code=\def\gradbars@format{value},
  value format/percent/.code=\def\gradbars@format{percent},
  overflow/.is choice,
  overflow/clip/.code=\def\gradbars@overflow{clip},
  overflow/error/.code=\def\gradbars@overflow{error},
  theme/.is choice,
  theme/blue/.style={fill=gradient,left color=gradbarsBlue,
    right color=gradbarsBlueLight,track color=gradbarsTrack,text color=gradbarsInk},
  theme/teal/.style={fill=gradient,left color=gradbarsTeal,
    right color=gradbarsTealLight,track color=gradbarsTrack,text color=gradbarsInk},
  theme/solid/.style={fill=solid,left color=gradbarsBlue,
    right color=gradbarsBlue,track color=gradbarsTrack,text color=gradbarsInk},
  theme/gray/.style={fill=solid,left color=black!65,
    right color=black!65,track color=black!8,text color=black},
  theme/lbyellow/.style={fill=gradient,left color=lightblue!90,
    right color=lightyellow!60,track color=gradbarsTrack,text color=gradbarsInk},
  theme/viblue/.style={fill=gradient,left color=violet!20,
    right color=lightblue!70,track color=gradbarsTrack,text color=gradbarsInk},
  theme/cyblu/.style={fill=gradient,left color=cyan,
    right color=blue!20,track color=gradbarsTrack,text color=gradbarsInk},
}
\pgfkeys{/gradbars,theme=blue}
\NewDocumentCommand\gradbarssetup{m}{\pgfkeys{/gradbars,#1}}
% Style definitions follow ordinary TeX grouping, like setup.
\NewDocumentCommand\gradbarsstyle{m m}{%
  \expandafter\def\csname gradbars@style@#1\endcsname{#2}}
\def\gradbarsUseStyle#1{%
  \ifcsname gradbars@style@#1\endcsname
    \ifnum\gradbars@styledepth<32
      \advance\gradbars@styledepth by 1
      \expandafter\let\expandafter\gradbars@styleoptions\csname gradbars@style@#1\endcsname
      \expandafter\pgfkeysalso\expandafter{\gradbars@styleoptions}%
      \advance\gradbars@styledepth by -1
    \else\PackageError{gradbars}{Style nesting exceeds 32 levels}{Check for recursive styles.}\fi
  \else\PackageError{gradbars}{Unknown style: #1}{Define it with gradbarsstyle first.}\fi
}

% Validate before drawing; floating point arithmetic also supports large counts.
\ExplSyntaxOn
\cs_new_protected:Npn \gradbarsCheckNumber #1#2
  {
    \tl_set:Nx \l_tmpa_tl {#1}
    \tl_trim_spaces:N \l_tmpa_tl
    \regex_match:nVTF { \A [\+\-]? ( \d+ (\.\d*)? | \.\d+ ) \Z }
      \l_tmpa_tl { }
      {
        \gradbars@validfalse
        \PackageError{gradbars}{Invalid~#2:~expected~a~decimal~number}
          {Use~0,~86.5,~or~125000.~Expressions~are~not~accepted.}
      }
  }

\cs_new_protected:Npn \gradbarsFail #1
  {\gradbars@validfalse \PackageError{gradbars}{#1}{Check~the~gradbars~options~and~data.}}
\cs_new_protected:Npn \gradbarsRange #1#2
  {
    \fp_compare:nT {#2 < \gradbars@min || #2 > \gradbars@max}
      {
        \str_if_eq:eeTF {\gradbars@overflow}{error}
          {\gradbarsFail{#1~exceeds~range}}
          {\PackageWarning{gradbars}{#1~exceeds~range;~bar~clipped,~label~unchanged}}
      }
  }
\cs_new:Npn \gradbarsPosition #1
  {\fp_eval:n {(max(\gradbars@min,min(\gradbars@max,#1))-\gradbars@min)/
    (\gradbars@max-\gradbars@min)}}
\clist_new:N \l__gradbars_thresholds_clist
\clist_new:N \l__gradbars_colors_clist
\cs_new_protected:Npn \gradbarsValidate
  {
    \gradbars@missingfalse
    \tl_set:Nx \l_tmpa_tl {\gradbars@value}
    \tl_trim_spaces:N \l_tmpa_tl
    \tl_if_empty:NT \l_tmpa_tl {\gradbars@missingtrue}
    \str_if_eq:VnT \l_tmpa_tl {NA} {\gradbars@missingtrue}
    \ifgradbars@missing \def\gradbars@value{0}\fi
    \gradbarsCheckNumber{\gradbars@value}{value}
    \gradbarsCheckNumber{\gradbars@min}{min}
    \gradbarsCheckNumber{\gradbars@max}{max}
    \tl_set:Nx \l_tmpa_tl {\gradbars@precision}
    \regex_match:nVTF {\A [0-6] \Z} \l_tmpa_tl { }
      {\gradbarsFail{Precision~must~be~an~integer~from~0~to~6}}
    \ifgradbars@valid
      \fp_compare:nNnF {\gradbars@max} > {0}
        {\gradbarsFail{Max~must~be~greater~than~zero}}
      \fp_compare:nNnT {\gradbars@min} > {0}
        {\gradbarsFail{Min~must~be~zero~or~negative}}
      \fp_compare:nNnT {\gradbars@min} = {0}
        {\fp_compare:nNnT {\gradbars@value} < {0}
          {\gradbarsFail{Negative~values~require~a~negative~min}}}
      \str_if_eq:eeT {\gradbars@format}{percent}
        {\fp_compare:nNnT {\gradbars@min} < {0}
          {\gradbarsFail{Percent~format~requires~min=0}}}
    \fi
    \tl_if_empty:oF {\gradbars@target}
      {\gradbarsCheckNumber{\gradbars@target}{target}}
    \gradbars@errorfalse
    \tl_if_empty:oF {\gradbars@errorminus}{\gradbars@errortrue}
    \tl_if_empty:oF {\gradbars@errorplus}{\gradbars@errortrue}
    \ifgradbars@error
      \tl_if_empty:oT {\gradbars@errorminus}{\def\gradbars@errorminus{0}}
      \tl_if_empty:oT {\gradbars@errorplus}{\def\gradbars@errorplus{0}}
      \gradbarsCheckNumber{\gradbars@errorminus}{error~minus}
      \gradbarsCheckNumber{\gradbars@errorplus}{error~plus}
    \fi
    \clist_set:Nx \l__gradbars_thresholds_clist {\gradbars@thresholds}
    \clist_set:Nx \l__gradbars_colors_clist {\gradbars@thresholdcolors}
    \clist_if_empty:NF \l__gradbars_thresholds_clist
      {
        \int_compare:nNnF {\clist_count:N \l__gradbars_thresholds_clist} = {2}
          {\gradbarsFail{Thresholds~must~contain~two~values}}
        \int_compare:nNnF {\clist_count:N \l__gradbars_colors_clist} = {3}
          {\gradbarsFail{Threshold~colors~must~contain~three~colors}}
        \clist_map_inline:Nn \l__gradbars_thresholds_clist
          {\gradbarsCheckNumber{##1}{threshold}}
      }
    \ifgradbars@valid
      \gradbarsRange{Value}{\gradbars@value}
      \tl_if_empty:oF {\gradbars@target}
        {
          \fp_compare:nT {\gradbars@target < \gradbars@min || \gradbars@target > \gradbars@max}
            {\gradbarsFail{Target~must~lie~within~range}}
          \cs_set:Npx \gradbars@targetpos {\gradbarsPosition{\gradbars@target}}
        }
      \ifgradbars@error
        \fp_compare:nT {\gradbars@errorminus<0 || \gradbars@errorplus<0}
          {\gradbarsFail{Error~magnitudes~must~be~nonnegative}}
        \ifgradbars@missing\else
        \gradbarsRange{Error~lower~endpoint}{\gradbars@value-\gradbars@errorminus}
        \gradbarsRange{Error~upper~endpoint}{\gradbars@value+\gradbars@errorplus}
        \cs_set:Npx \gradbars@errorlo
          {\gradbarsPosition{\gradbars@value-\gradbars@errorminus}}
        \cs_set:Npx \gradbars@errorhi
          {\gradbarsPosition{\gradbars@value+\gradbars@errorplus}}
        \fi
      \fi
      \cs_set:Npx \gradbars@zero {\gradbarsPosition{0}}
      \cs_set:Npx \gradbars@end {\gradbarsPosition{\gradbars@value}}
      \cs_set:Npx \gradbars@start {\fp_eval:n {min(\gradbars@zero,\gradbars@end)}}
      \cs_set:Npx \gradbars@stop {\fp_eval:n {max(\gradbars@zero,\gradbars@end)}}
      \fp_compare:nNnT {\gradbars@value} < {0}
        {\gradbars@gradientfalse \let\gradbars@left\gradbars@negativecolor}
      \clist_if_empty:NF \l__gradbars_thresholds_clist
        {
          \fp_compare:nNnF {\clist_item:Nn \l__gradbars_thresholds_clist {1}}
            < {\clist_item:Nn \l__gradbars_thresholds_clist {2}}
            {\gradbarsFail{Thresholds~must~be~strictly~increasing}}
          \int_set:Nn \l_tmpa_int {1}
          \fp_compare:nNnF {\gradbars@value}
            < {\clist_item:Nn \l__gradbars_thresholds_clist {1}}
            {\int_set:Nn \l_tmpa_int {2}}
          \fp_compare:nNnF {\gradbars@value}
            < {\clist_item:Nn \l__gradbars_thresholds_clist {2}}
            {\int_set:Nn \l_tmpa_int {3}}
          \cs_set:Npx \gradbars@left
            {\clist_item:Nn \l__gradbars_colors_clist
              {\str_if_eq:eeTF {\gradbars@better}{lower}{4-\l_tmpa_int}{\l_tmpa_int}}}
          \gradbars@gradientfalse
        }
      \str_if_eq:eeTF {\gradbars@format}{percent}
        {\cs_set:Npx \gradbars@number
          {\fp_eval:n {100*\gradbars@value/\gradbars@max}}}
        {\cs_set:Npx \gradbars@number
          {\fp_eval:n {\gradbars@value}}}
    \fi
  }
\clist_new:N \l__gradbars_band_clist
\clist_new:N \l__gradbars_stack_clist
\clist_new:N \l__gradbars_stackcolors_clist
\fp_new:N \l__gradbars_sum_fp
\fp_new:N \l__gradbars_before_fp
\cs_new_protected:Npn \gradbarsValidateExtras
  {
    \clist_set:Nx \l__gradbars_band_clist {\gradbars@band}
    \clist_if_empty:NF \l__gradbars_band_clist
      {
        \tl_set:Nx \l_tmpa_tl {\gradbars@band}
        \regex_match:nVT {\A\s*,|,\s*,|,\s*\Z} \l_tmpa_tl
          {\gradbarsFail{Band~must~contain~two~endpoints}}
        \int_compare:nNnF {\clist_count:N \l__gradbars_band_clist} = {2}
          {\gradbarsFail{Band~must~contain~two~endpoints}}
        \clist_map_inline:Nn \l__gradbars_band_clist
          {\gradbarsCheckNumber{##1}{band~endpoint}}
        \gradbarsCheckNumber{\gradbars@bandopacity}{band~opacity}
        \ifgradbars@valid
          \cs_set:Npx \gradbars@bandlo {\clist_item:Nn \l__gradbars_band_clist {1}}
          \cs_set:Npx \gradbars@bandhi {\clist_item:Nn \l__gradbars_band_clist {2}}
          \fp_compare:nF {\gradbars@min <= \gradbars@bandlo < \gradbars@bandhi <= \gradbars@max}
            {\gradbarsFail{Band~must~be~increasing~and~within~range}}
          \fp_compare:nF {0 <= \gradbars@bandopacity <= 1}
            {\gradbarsFail{Band~opacity~must~be~between~zero~and~one}}
        \fi
      }
    \ifgradbars@stack
      \ifgradbars@valid
        \gradbarsRange{Positive~stack~total}{\gradbars@positive}
        \gradbarsRange{Negative~stack~total}{\gradbars@negative}
        \fp_compare:nNnT {\gradbars@negative} < {0}
          {\fp_compare:nNnT {\gradbars@min} = {0}
             {\gradbarsFail{Negative~stack~requires~a~negative~min}}}
        \cs_set:Npx \gradbars@start {\gradbarsPosition{\gradbars@negative}}
        \cs_set:Npx \gradbars@stop {\gradbarsPosition{\gradbars@positive}}
      \fi
      \tl_if_empty:oF {\gradbars@thresholds}
        {\gradbarsFail{Stacked~bars~do~not~support~thresholds}}
      \ifgradbars@error \gradbarsFail{Stacked~bars~do~not~support~error~whiskers}\fi
      \clist_set:Nx \l__gradbars_stackcolors_clist {\gradbars@stackcolors}
      \clist_if_empty:NT \l__gradbars_stackcolors_clist
        {\gradbarsFail{Stack~colors~must~not~be~empty}}
    \fi
  }
\cs_new_protected:Npn \gradbarsDrawBand
  {
    \clist_if_empty:NF \l__gradbars_band_clist
      {
        \pgfmathsetlengthmacro\gradbars@bandxlo{\gradbarsPosition{\gradbars@bandlo}*(\gradbars@width)}
        \pgfmathsetlengthmacro\gradbars@bandxhi{\gradbarsPosition{\gradbars@bandhi}*(\gradbars@width)}
        \path[fill=\gradbars@bandcolor,fill~opacity=\gradbars@bandopacity,draw=none]
          (\gradbars@bandxlo,-2pt) rectangle ([yshift=2pt]\gradbars@bandxhi,\gradbars@height);
      }
  }
\cs_new_protected:Npn \gradbarsParseStack #1
  {
    \tl_set:Nx \l_tmpa_tl {#1}
    % Empty elements are invalid, not silently dropped by clist normalization.
    \regex_match:nVTF {\A\s*\Z|\A\s*,|,\s*,|,\s*\Z} \l_tmpa_tl
      {\gradbarsFail{Stack~requires~a~nonempty~list~of~numbers}} { }
    \clist_set:Nx \l__gradbars_stack_clist {#1}
    \fp_zero:N \l__gradbars_sum_fp
    \fp_zero:N \l__gradbars_positive_fp
    \fp_zero:N \l__gradbars_negative_fp
    \clist_map_inline:Nn \l__gradbars_stack_clist
      {
        \gradbarsCheckNumber{##1}{stack~segment}
        \ifgradbars@valid
          \fp_add:Nn \l__gradbars_sum_fp {##1}
          \fp_compare:nNnTF {##1} < {0}
            {\fp_add:Nn \l__gradbars_negative_fp {##1}}
            {\fp_add:Nn \l__gradbars_positive_fp {##1}}
        \fi
      }
    \cs_set:Npx \gradbars@total {\fp_to_decimal:N \l__gradbars_sum_fp}
    \cs_set:Npx \gradbars@positive {\fp_to_decimal:N \l__gradbars_positive_fp}
    \cs_set:Npx \gradbars@negative {\fp_to_decimal:N \l__gradbars_negative_fp}
    \cs_set:Npx \gradbars@gross {\fp_eval:n {\gradbars@positive-\gradbars@negative}}
  }
\cs_new_protected:Npn \gradbarsDrawStack
  {
    \gradbarsStackReset
    \int_zero:N \l_tmpa_int
    \clist_map_inline:Nn \l__gradbars_stack_clist
      {
        \gradbarsStackStep{##1}
        \int_incr:N \l_tmpa_int
        \gradbarsCategoryColor{\l_tmpa_int}
        \pgfmathsetlengthmacro\gradbars@segmentlo
          {\gradbarsPosition{\l__gradbars_before_fp}*(\gradbars@width)}
        \pgfmathsetlengthmacro\gradbars@segmenthi
          {\gradbarsPosition{\l__gradbars_sum_fp}*(\gradbars@width)}
        \ifdim\gradbars@segmenthi>\gradbars@segmentlo\relax
          \ifgradbars@outline
            \draw[draw=\gradbars@segmentcolor,line~width=\gradbars@outlinewidth]
              (\gradbars@segmentlo,0) rectangle (\gradbars@segmenthi,\gradbars@height);
          \else
            \path[fill=\gradbars@segmentcolor,draw=none]
              (\gradbars@segmentlo,0) rectangle (\gradbars@segmenthi,\gradbars@height);
            \gradbarsPatternRect{\l_tmpa_int}{\gradbars@segmentlo}{0}{\gradbars@segmenthi}{\gradbars@height}
          \fi
        \fi
      }
  }
\ExplSyntaxOff

\def\gradbars@labelcontent{%
  \ifx\gradbars@text\@empty
    \ifgradbars@missing\gradbars@missingtext\else
      \begingroup
      \def\gradbars@temp{scientific}%
      \ifx\gradbars@numberformat\gradbars@temp
        \pgfmathprintnumber[sci,sci zerofill,precision=\gradbars@precision]{\gradbars@number}%
      \else
        \def\gradbars@temp{grouped}%
        \ifx\gradbars@numberformat\gradbars@temp
          \pgfmathprintnumber[fixed,fixed zerofill,precision=\gradbars@precision,1000 sep={\mathord{,}}]{\gradbars@number}%
        \else
          \pgfmathprintnumber[fixed,fixed zerofill,precision=\gradbars@precision,1000 sep={\,}]{\gradbars@number}%
        \fi
      \fi
      \def\gradbars@temp{percent}%
      \ifx\gradbars@format\gradbars@temp\%\else\gradbars@unit\fi
      \endgroup
    \fi
  \else\gradbars@text\fi
}

\def\gradbars@draw{%
  \tikzset{gradbars label background/.style={}}%
  \ifgradbars@print
    \tikzset{gradbars label background/.style={fill=white,inner sep=1pt}}%
  \fi
  \pgfmathsetlengthmacro\gradbars@xzero{\gradbars@zero*(\gradbars@width)}%
  \pgfmathsetlengthmacro\gradbars@xend{\gradbars@end*(\gradbars@width)}%
  \pgfmathsetlengthmacro\gradbars@xstart{\gradbars@start*(\gradbars@width)}%
  \pgfmathsetlengthmacro\gradbars@xstop{\gradbars@stop*(\gradbars@width)}%
  \pgfmathsetlengthmacro\gradbars@radius{min(\gradbars@rounded,.5*(\gradbars@height),.5*(\gradbars@width))}%
  \gradbarsDrawTrack
  \gradbarsDrawBand
  \gradbarsDrawShape
  \ifgradbars@spark\else\ifdim\gradbars@xzero>0pt
    \draw[gradbarsInk!65,line width=.35pt]
      (\gradbars@xzero,0)--(\gradbars@xzero,\gradbars@height);
  \fi\fi
  \ifx\gradbars@target\@empty\else
    \pgfmathsetlengthmacro\gradbars@xtarget{\gradbars@targetpos*(\gradbars@width)}%
    \draw[draw=\gradbars@targetcolor,line width=.6pt]
      ([yshift=-1pt]\gradbars@xtarget,0)--
      ([yshift=1pt]\gradbars@xtarget,\gradbars@height);
  \fi
  \ifgradbars@missing\else\ifgradbars@error
    \pgfmathsetlengthmacro\gradbars@xlo{\gradbars@errorlo*(\gradbars@width)}%
    \pgfmathsetlengthmacro\gradbars@xhi{\gradbars@errorhi*(\gradbars@width)}%
    \draw[draw=\gradbars@errorcolor,line width=.5pt]
      ([yshift=.5*\gradbars@height]\gradbars@xlo,0)--
      ([yshift=.5*\gradbars@height]\gradbars@xhi,0);
    \foreach \gradbars@cap in {\gradbars@xlo,\gradbars@xhi}{
      \draw[draw=\gradbars@errorcolor,line width=.5pt]
        ([yshift=.2*\gradbars@height]\gradbars@cap,0)--
        ([yshift=.8*\gradbars@height]\gradbars@cap,0);
    }
  \fi\fi
  \gradbarsDrawReferences
  \gradbarsDrawOverflow
  \gradbarsDrawChange
  \gradbarsResetLabels
  \ifgradbars@stack
    \gradbarsDrawSegmentLabels
    \ifgradbars@legend\gradbarsDrawLegend\fi
  \fi
  \gradbarsDrawMainLabel
  \path[use as bounding box] (0,-\gradbars@rowpadding)
    rectangle (\gradbars@width,{\gradbars@height+\gradbars@rowpadding});
}
\def\gradbars@render{%
  \gradbars@validtrue
  \gradbarsResolveShapeLengths
  \gradbarsValidate
  \gradbarsValidateExtras
  \ifdim\gradbars@rounded<0pt
    \gradbars@validfalse
    \PackageError{gradbars}{Rounded radius must be nonnegative}{Use rounded=2pt.}%
  \fi
  \ifdim\gradbars@width>0pt\else
    \gradbars@validfalse
    \PackageError{gradbars}{Width must be positive}{Use a length such as 24mm.}%
  \fi
  \ifdim\gradbars@height>0pt\else
    \gradbars@validfalse
    \PackageError{gradbars}{Height must be positive}{Use a length such as 1.5ex.}%
  \fi
  \ifgradbars@valid
    \ifpgfpicture
      \begin{scope}\gradbars@draw\end{scope}%
    \else
      \gradbarsPrepareAlt
      \tikz[baseline={([yshift=-.5ex]0,{.5*\gradbars@height})}]{\gradbars@draw}%
    \fi
  \fi
}
\NewDocumentCommand\gradbar{O{} m}{%
  \leavevmode\begingroup
    \gradbarsCaptureTikzAlt
    \gradbars@capturealttrue
    \pgfkeys{/gradbars,#1}%
    \gradbars@capturealtfalse
    % Resolve font-relative dimensions in the surrounding text font.
    \ifpgfpicture
      \pgfinterruptpicture
      \font@name
      \edef\gradbars@resolved{%
        \noexpand\endpgfinterruptpicture
        \def\noexpand\gradbars@width{\the\dimexpr\gradbars@width\relax}%
        \def\noexpand\gradbars@height{\the\dimexpr\gradbars@height\relax}%
        \def\noexpand\gradbars@rounded{\the\dimexpr\gradbars@rounded\relax}%
        \def\noexpand\gradbars@gap{\the\dimexpr\gradbars@gap\relax}%
        \def\noexpand\gradbars@labelwidth{\the\dimexpr\gradbars@labelwidth\relax}%
      }%
      \gradbars@resolved
    \fi
    \def\gradbars@value{#2}%
    \gradbars@render
  \endgroup
}
\NewDocumentCommand\gradstack{O{} m}{%
  \leavevmode\begingroup
    \gradbars@validtrue
    \gradbarsParseStack{#2}%
    \ifgradbars@valid
      \gradbars@stacktrue
      \gradbar[#1]{\gradbars@total}%
    \fi
  \endgroup
}

% v0.0.2: labels, legends and collected table columns.
\def\gradbars@stacknames{}
\def\gradbars@segmentlabels{none}
\def\gradbars@segmentfont{\scriptsize}
\def\gradbars@segmenttextcolor{auto}
\def\gradbars@labeloverflow{auto}
\newif\ifgradbars@legend
\newif\ifgradbars@inside
\newif\ifgradbars@fit
\newsavebox\gradbars@segmentbox
\pgfkeys{/gradbars,
  stack names/.store in=\gradbars@stacknames,
  legend/.is if=gradbars@legend,legend/.default=true,
  segment font/.store in=\gradbars@segmentfont,
  segment text color/.store in=\gradbars@segmenttextcolor,
  segment labels/.is choice,
  segment labels/none/.code=\def\gradbars@segmentlabels{none},
  segment labels/value/.code=\def\gradbars@segmentlabels{value},
  segment labels/percent/.code=\def\gradbars@segmentlabels{percent},
  segment labels/name/.code=\def\gradbars@segmentlabels{name},
  segment labels/name value/.code=\def\gradbars@segmentlabels{name value},
  segment labels/name percent/.code=\def\gradbars@segmentlabels{name percent},
  label overflow/.is choice,
  label overflow/auto/.code=\def\gradbars@labeloverflow{auto},
  label overflow/allow/.code=\def\gradbars@labeloverflow{allow}
}
\def\gradbarsMeasure#1#2#3{%
  \sbox#1{\pgfinterruptpicture\font@name #2#3\endpgfinterruptpicture}}

\ExplSyntaxOn
\clist_new:N \l__gradbars_names_clist
\seq_new:N \l__gradbars_occupied_seq
\dim_new:N \l__gradbars_top_dim
\dim_new:N \l__gradbars_labelbottom_dim
\dim_new:N \l__gradbars_labelleft_dim
\dim_new:N \l__gradbars_labelright_dim
\dim_new:N \l__gradbars_leaderx_dim
\dim_new:N \l__gradbars_leaderleft_dim
\dim_new:N \l__gradbars_leaderright_dim
\fp_new:N \l__gradbars_luminance_fp
\int_new:N \l__gradbars_index_int
\cs_new_protected:Npn \gradbarsContrast #1
  {
    \convertcolorspec{named}{#1}{rgb}\gradbars@rgb
    \clist_set:Nx \l_tmpa_clist {\gradbars@rgb}
    \fp_zero:N \l__gradbars_luminance_fp
    \int_step_inline:nn {3}
      {
        \fp_set:Nn \l_tmpa_fp {\clist_item:Nn \l_tmpa_clist {##1}}
        \fp_compare:nNnTF {\l_tmpa_fp} > {0.04045}
          {\fp_set:Nn \l_tmpa_fp {((\l_tmpa_fp+0.055)/1.055)^2.4}}
          {\fp_set:Nn \l_tmpa_fp {\l_tmpa_fp/12.92}}
        \fp_add:Nn \l__gradbars_luminance_fp
          {\l_tmpa_fp*\clist_item:nn {0.2126,0.7152,0.0722}{##1}}
      }
    \fp_compare:nNnTF {\l__gradbars_luminance_fp} > {0.179}
      {\def\gradbars@labelcolor{black}}{\def\gradbars@labelcolor{white}}
  }
\let\gradbarsValidatePrevious\gradbarsValidateExtras
\cs_set_protected:Npn \gradbarsValidateExtras
  {
    \gradbarsValidatePrevious
    \clist_set:No \l__gradbars_names_clist {\gradbars@stacknames}
    \ifgradbars@stack
      \clist_if_empty:NF \l__gradbars_names_clist
        {\int_compare:nNnF {\clist_count:N \l__gradbars_names_clist}
          = {\clist_count:N \l__gradbars_stack_clist}
          {\gradbarsFail{Stack~names~must~match~the~segment~count}}}
      \bool_set_false:N \l_tmpa_bool
      \ifgradbars@legend \bool_set_true:N \l_tmpa_bool \fi
      \str_case_e:nn {\gradbars@segmentlabels}
        {{name}{\bool_set_true:N \l_tmpa_bool}
         {name~value}{\bool_set_true:N \l_tmpa_bool}
         {name~percent}{\bool_set_true:N \l_tmpa_bool}}
      \bool_if:NT \l_tmpa_bool
        {\clist_if_empty:NT \l__gradbars_names_clist
          {\gradbarsFail{Stack~names~are~required~for~names~or~legend}}}
      \ifgradbars@legend
        \ifdim\gradbars@width>14pt\relax\else
          \gradbarsFail{Legend~width~must~exceed~14pt}\fi
      \fi
    \else
      \ifgradbars@legend \gradbarsFail{Automatic~legend~requires~gradstack}\fi
    \fi
  }
\cs_new_protected:Npn \gradbarsResetLabels
  {\seq_clear:N \l__gradbars_occupied_seq
   \dim_set:Nn \l__gradbars_top_dim {\gradbars@height+4pt}}
\cs_new_protected:Npn \gradbarsCheckOverlap #1#2#3
  {
    \dim_compare:nNnT {\l__gradbars_labelleft_dim} < {#2+3pt}
      {\dim_compare:nNnT {\l__gradbars_labelright_dim} > {#1-3pt}
        {\dim_set:Nn \l__gradbars_labelbottom_dim
          {\dim_max:nn {\l__gradbars_labelbottom_dim}{#3+3pt}}}}
  }
\cs_new_protected:Npn \gradbarsLeaderBounds #1#2#3
  {
    \dim_set:Nn \l__gradbars_leaderleft_dim
      {\dim_min:nn {\l__gradbars_leaderleft_dim}{#1-4pt}}
    \dim_set:Nn \l__gradbars_leaderright_dim
      {\dim_max:nn {\l__gradbars_leaderright_dim}{#2+4pt}}
  }
\cs_new_protected:Npn \gradbarsPlaceSegment
  {
    \pgfmathsetlengthmacro\gradbars@segmentmid{.5*(\gradbars@segmentlo+\gradbars@segmenthi)}
    \gradbars@fittrue
    \ifgradbars@outline\gradbars@fitfalse\fi
    \ifdim\dimexpr\wd\gradbars@segmentbox+4pt\relax>\dimexpr\gradbars@segmenthi-\gradbars@segmentlo\relax
      \gradbars@fitfalse\fi
    \ifdim\dimexpr\ht\gradbars@segmentbox+\dp\gradbars@segmentbox+2pt\relax>\gradbars@height\relax
      \gradbars@fitfalse\fi
    \ifx\gradbars@target\@empty\else
      \pgfmathsetlengthmacro\gradbars@distance{abs(\gradbars@xtarget-\gradbars@segmentmid)}
      \ifdim\gradbars@distance<\dimexpr.5\wd\gradbars@segmentbox+2pt\relax
        \gradbars@fitfalse\fi
    \fi
    \ifgradbars@fit
      \let\gradbars@labelcolor\gradbars@segmenttextcolor
      \str_if_eq:eeT {\gradbars@segmenttextcolor}{auto}{\gradbarsContrast{\gradbars@segmentcolor}}
      \gradbarsMeasure\gradbars@segmentbox\gradbars@segmentfont{\color{\gradbars@labelcolor}\gradbars@segmentcontent}
      \node[inner~sep=0pt,outer~sep=0pt,text=\gradbars@labelcolor]
        [gradbars~label~background]
        at (\gradbars@segmentmid,.5*\gradbars@height){\usebox\gradbars@segmentbox};
    \else
      \pgfmathsetlengthmacro\gradbars@labelx
        {max(.5*\wd\gradbars@segmentbox,min(\gradbars@segmentmid,\gradbars@width-.5*\wd\gradbars@segmentbox))}
      \dim_set:Nn \l__gradbars_labelleft_dim {\gradbars@labelx-.5\wd\gradbars@segmentbox}
      \dim_set:Nn \l__gradbars_labelright_dim {\gradbars@labelx+.5\wd\gradbars@segmentbox}
      \dim_set:Nn \l__gradbars_labelbottom_dim {\gradbars@height+4pt}
      \seq_map_inline:Nn \l__gradbars_occupied_seq {\gradbarsCheckOverlap ##1}
      % Route elevated leaders around earlier label boxes, not through text.
      \dim_set:Nn \l__gradbars_leaderx_dim {\gradbars@segmentmid}
      \dim_compare:nNnT {\l__gradbars_labelbottom_dim} > {\gradbars@height+4pt}
        {
          \dim_set:Nn \l__gradbars_leaderleft_dim {\gradbars@segmentmid}
          \dim_set:Nn \l__gradbars_leaderright_dim {\gradbars@segmentmid}
          \seq_map_inline:Nn \l__gradbars_occupied_seq {\gradbarsLeaderBounds ##1}
          \dim_compare:nNnTF
            {\gradbars@segmentmid-\l__gradbars_leaderleft_dim} <
            {\l__gradbars_leaderright_dim-\gradbars@segmentmid}
            {\dim_set_eq:NN \l__gradbars_leaderx_dim \l__gradbars_leaderleft_dim}
            {\dim_set_eq:NN \l__gradbars_leaderx_dim \l__gradbars_leaderright_dim}
        }
      \seq_put_right:Nx \l__gradbars_occupied_seq
        {{\dim_use:N \l__gradbars_labelleft_dim}{\dim_use:N \l__gradbars_labelright_dim}
          {\dim_eval:n {\l__gradbars_labelbottom_dim+\ht\gradbars@segmentbox+\dp\gradbars@segmentbox}}}
      \dim_set:Nn \l__gradbars_top_dim {\dim_max:nn {\l__gradbars_top_dim}
        {\l__gradbars_labelbottom_dim+\ht\gradbars@segmentbox+\dp\gradbars@segmentbox+3pt}}
      \draw[gradbarsInk!55,line~width=.3pt]
        (\gradbars@segmentmid,\gradbars@height)--
        (\gradbars@segmentmid,\dim_eval:n {\gradbars@height+1pt})--
        (\dim_use:N \l__gradbars_leaderx_dim,\dim_eval:n {\gradbars@height+1pt})--
        (\dim_use:N \l__gradbars_leaderx_dim,\dim_eval:n {\l__gradbars_labelbottom_dim-2pt})--
        (\gradbars@labelx,\dim_eval:n {\l__gradbars_labelbottom_dim-2pt});
      \let\gradbars@labelcolor\gradbars@segmenttextcolor
      \str_if_eq:eeT {\gradbars@segmenttextcolor}{auto}{\def\gradbars@labelcolor{black}}
      \gradbarsMeasure\gradbars@segmentbox\gradbars@segmentfont{\color{\gradbars@labelcolor}\gradbars@segmentcontent}
      \node[anchor=south,inner~sep=0pt,outer~sep=0pt,text=\gradbars@labelcolor]
        at (\gradbars@labelx,\dim_use:N \l__gradbars_labelbottom_dim){\usebox\gradbars@segmentbox};
    \fi
  }
\cs_new_protected:Npn \gradbarsDrawSegmentLabels
  {
    \str_if_eq:eeF {\gradbars@segmentlabels}{none}
      {
        \gradbarsStackReset
        \int_zero:N \l__gradbars_index_int
        \clist_map_inline:Nn \l__gradbars_stack_clist
          {
            \int_incr:N \l__gradbars_index_int
            \gradbarsStackStep{##1}
            \pgfmathsetlengthmacro\gradbars@segmentlo{\gradbarsPosition{\l__gradbars_before_fp}*(\gradbars@width)}
            \pgfmathsetlengthmacro\gradbars@segmenthi{\gradbarsPosition{\l__gradbars_sum_fp}*(\gradbars@width)}
            \ifdim\gradbars@segmenthi>\gradbars@segmentlo\relax
              \gradbarsCategoryColor{\l__gradbars_index_int}
              \def\gradbars@segmentcontent
              {\def\gradbars@text{}\def\gradbars@unit{}\def\gradbars@format{value}
                \cs_set:Npx \gradbars@number {\fp_eval:n {##1}}
                \str_case_e:nn {\gradbars@segmentlabels}
                  {{percent}{\def\gradbars@format{percent}}
                   {name~percent}{\def\gradbars@format{percent}}}
                \str_if_eq:eeT {\gradbars@format}{percent}
                  {\cs_set:Npx \gradbars@number {\fp_eval:n {100*abs(##1)/\gradbars@gross}}}
                \tl_clear:N \l_tmpb_tl
                \str_case_e:nn {\gradbars@segmentlabels}
                  {{name}{\tl_set:Nn \l_tmpb_tl {name}}
                   {name~value}{\tl_set:Nn \l_tmpb_tl {both}}
                   {name~percent}{\tl_set:Nn \l_tmpb_tl {both}}}
                \tl_if_empty:NF \l_tmpb_tl
                    {\clist_item:Nn \l__gradbars_names_clist {\l__gradbars_index_int}}
                   \str_if_eq:VnT \l_tmpb_tl {both}{:\space}
                   \str_if_eq:VnF \l_tmpb_tl {name}{\gradbarsSingleLabel}}
              \gradbarsMeasure\gradbars@segmentbox\gradbars@segmentfont{\gradbars@segmentcontent}
              \gradbarsPlaceSegment
            \fi
          }
      }
  }
\cs_new_protected:Npn \gradbarsDrawLegend
  {
    \dim_set:Nn \l_tmpa_dim {6pt}
    \int_zero:N \l__gradbars_index_int
    \clist_map_inline:Nn \l__gradbars_names_clist
      {
        \int_incr:N \l__gradbars_index_int
        \gradbarsCategoryColor{\l__gradbars_index_int}
        \gradbarsMeasure\gradbars@segmentbox\gradbars@font
          {\parbox[t]{\dimexpr\gradbars@width-14pt\relax}{\raggedright\strut ##1\strut}}
        \ifgradbars@outline
          \draw[draw=\gradbars@segmentcolor,line~width=\gradbars@outlinewidth]
            (0,-\dim_use:N \l_tmpa_dim) rectangle (8pt,-\dim_eval:n {\l_tmpa_dim+6pt});
        \else
          \path[fill=\gradbars@segmentcolor,draw=none]
            (0,-\dim_use:N \l_tmpa_dim) rectangle (8pt,-\dim_eval:n {\l_tmpa_dim+6pt});
          \gradbarsPatternRect{\l__gradbars_index_int}{0}{-\dim_use:N \l_tmpa_dim}
            {8pt}{-\dim_eval:n {\l_tmpa_dim+6pt}}
        \fi
        \node[anchor=north~west,inner~sep=0pt,outer~sep=0pt,text=gradbarsInk]
          at (14pt,-\dim_use:N \l_tmpa_dim){\usebox\gradbars@segmentbox};
        \dim_add:Nn \l_tmpa_dim {\ht\gradbars@segmentbox+\dp\gradbars@segmentbox+4pt}
      }
  }
\cs_new_protected:Npn \gradbarsMainBackground
  {
    \def\gradbars@labelcolor{black}
    \ifgradbars@inside
      \let\gradbars@background\gradbars@track
      \clist_if_empty:NF \l__gradbars_band_clist
        {\dim_compare:nNnT {\gradbars@labelx} > {\gradbars@bandxlo}
          {\dim_compare:nNnT {\gradbars@labelx} < {\gradbars@bandxhi}
            {\edef\gradbars@background{\gradbars@bandcolor!
               \fp_eval:n {100*\gradbars@bandopacity}!\gradbars@track}}}}
      \ifdim\gradbars@labelx>\gradbars@xstart\relax
        \ifdim\gradbars@labelx<\gradbars@xstop\relax
          \let\gradbars@background\gradbars@left
          \ifgradbars@gradient
            \pgfmathparse{100*(\gradbars@labelx-\gradbars@xstart)/(\gradbars@xstop-\gradbars@xstart)}
            \edef\gradbars@background{\gradbars@right!\pgfmathresult!\gradbars@left}
          \fi
          \ifgradbars@stack
            \fp_zero:N \l__gradbars_sum_fp
            \int_zero:N \l__gradbars_index_int
            \clist_map_inline:Nn \l__gradbars_stack_clist
              {\fp_add:Nn \l__gradbars_sum_fp {##1}\int_incr:N \l__gradbars_index_int
               \pgfmathsetlengthmacro\gradbars@segmenthi{\gradbarsPosition{\l__gradbars_sum_fp}*(\gradbars@width)}
               \dim_compare:nNnT {\gradbars@labelx} < {\gradbars@segmenthi}
               {
                 \gradbarsCategoryColor{\l__gradbars_index_int}
                 \let\gradbars@background\gradbars@segmentcolor
                 \clist_map_break:
               }}
          \fi
        \fi
      \fi
      \gradbarsContrast{\gradbars@background}
    \fi
  }
\cs_new_protected:Npn \gradbarsDrawMainLabel
  {
    \str_if_eq:eeF {\gradbars@label}{none}
      {
        \gradbarsMeasure\gradbars@labelbox\gradbars@font{\gradbars@labelcontent}
        \let\gradbars@placement\gradbars@label
        \gradbars@insidefalse
        \str_if_eq:eeT {\gradbars@placement}{auto}
          {\def\gradbars@placement{outside}
           \ifdim\dimexpr\wd\gradbars@labelbox+4pt\relax<\dimexpr\gradbars@xstop-\gradbars@xstart\relax
             \def\gradbars@placement{center}\fi}
        \str_if_eq:eeF {\gradbars@labeloverflow}{allow}
          {\str_if_eq:eeT {\gradbars@placement}{inside}
             {\ifdim\dimexpr\wd\gradbars@labelbox+4pt\relax>\gradbars@width\relax
                \def\gradbars@placement{outside}\fi}
           \str_if_eq:eeT {\gradbars@placement}{end}
             {\ifdim\wd\gradbars@labelbox>\gradbars@width\relax
                \def\gradbars@placement{outside}\fi}}
        \str_case_e:nn {\gradbars@placement}
          {{inside}{\gradbars@insidetrue}{center}{\gradbars@insidetrue}}
        \ifgradbars@inside
          \str_if_eq:eeF {\gradbars@shape}{bar}
            {\def\gradbars@placement{outside}\gradbars@insidefalse}
          \ifgradbars@stack
            \fp_compare:nNnT {\gradbars@negative} < {0}
              {\def\gradbars@placement{outside}\gradbars@insidefalse}
          \fi
          \ifgradbars@compare \def\gradbars@placement{outside}\gradbars@insidefalse\fi
          \ifgradbars@outline \def\gradbars@placement{outside}\gradbars@insidefalse\fi
          \ifgradbars@rangemode
            \tl_if_empty:oF {\gradbars@rangepoint}
              {\def\gradbars@placement{outside}\gradbars@insidefalse}
          \fi
        \fi
        \ifgradbars@inside
          \str_if_eq:eeF {\gradbars@labeloverflow}{allow}
            {\ifdim\dimexpr\ht\gradbars@labelbox+\dp\gradbars@labelbox+2pt\relax>\gradbars@height\relax
               \def\gradbars@placement{outside}\gradbars@insidefalse\fi}
          \ifgradbars@stack
            \str_if_eq:eeF {\gradbars@segmentlabels}{none}
              {\def\gradbars@placement{outside}\gradbars@insidefalse}
          \fi
          \ifgradbars@error \def\gradbars@placement{outside}\gradbars@insidefalse\fi
          \ifgradbars@inside
            \str_if_eq:eeTF {\gradbars@placement}{center}
              {\pgfmathsetlengthmacro\gradbars@labelx{.5*(\gradbars@xstart+\gradbars@xstop)}}
              {\pgfmathsetlengthmacro\gradbars@labelx{\gradbars@width-2pt-.5*\wd\gradbars@labelbox}}
            \ifx\gradbars@target\@empty\else
              \pgfmathsetlengthmacro\gradbars@distance{abs(\gradbars@xtarget-\gradbars@labelx)}
              \ifdim\gradbars@distance<\dimexpr.5\wd\gradbars@labelbox+2pt\relax
                \def\gradbars@placement{outside}\gradbars@insidefalse\fi
            \fi
          \fi
        \fi
        \let\gradbars@labelcolor\gradbars@textcolor
        \str_if_eq:eeT {\gradbars@textcolor}{auto}{\gradbarsMainBackground}
        \gradbarsMeasure\gradbars@labelbox\gradbars@font{\color{\gradbars@labelcolor}\gradbars@labelcontent}
        \ifgradbars@inside
          \node[inner~sep=0pt,outer~sep=0pt,text=\gradbars@labelcolor]
            [gradbars~label~background]
            at (\gradbars@labelx,.5*\gradbars@height){\usebox\gradbars@labelbox};
        \else
          \str_if_eq:eeTF {\gradbars@placement}{end}
            {\pgfmathsetlengthmacro\gradbars@labelx
              {max(.5*\wd\gradbars@labelbox,min(\gradbars@xend,\gradbars@width-.5*\wd\gradbars@labelbox))}
             \node[anchor=south,inner~sep=0pt,outer~sep=0pt,text=\gradbars@labelcolor]
               at (\gradbars@labelx,\dim_use:N \l__gradbars_top_dim){\usebox\gradbars@labelbox};}
            {\dim_set:Nn \l_tmpa_dim {\gradbars@labelwidth}
             \str_if_eq:eeF {\gradbars@labeloverflow}{allow}
               {\dim_set:Nn \l_tmpa_dim {\dim_max:nn {\l_tmpa_dim}{\wd\gradbars@labelbox}}}
             \node[anchor=west,inner~sep=0pt,outer~sep=0pt,text=\gradbars@labelcolor]
               at ([xshift=\gradbars@gap,yshift=.5*\gradbars@height]\gradbars@width,0)
               {\makebox[\dim_use:N \l_tmpa_dim][r]{\usebox\gradbars@labelbox}};}
        \fi
      }
  }
\ExplSyntaxOff
\NewDocumentCommand\gradbarslegend{O{} m}{%
  \begingroup\gradbarsCaptureTikzAlt\gradbars@capturealttrue
  \pgfkeys{/gradbars,#1,stack names={#2}}\gradbars@capturealtfalse%
  \begingroup\font@name
    \edef\gradbars@next{\endgroup
      \def\noexpand\gradbars@width{\the\dimexpr\gradbars@width\relax}}%
  \gradbars@next
  \gradbarsResolveShapeLengths
  \gradbarsStandaloneLegend
  \endgroup}
\ExplSyntaxOn
\cs_new_protected:Npn \gradbarsStandaloneLegend
  {
    \gradbars@validtrue
    \clist_set:No \l__gradbars_names_clist {\gradbars@stacknames}
    \clist_set:No \l__gradbars_stackcolors_clist {\gradbars@stackcolors}
    \clist_if_empty:NT \l__gradbars_names_clist {\gradbarsFail{Legend~names~must~not~be~empty}}
    \clist_if_empty:NT \l__gradbars_stackcolors_clist {\gradbarsFail{Stack~colors~must~not~be~empty}}
    \gradbarsValidatePatterns
    \ifdim\gradbars@width>14pt\else\gradbarsFail{Legend~width~must~exceed~14pt}\fi
    \ifgradbars@valid
      \ifpgfpicture \gradbarsDrawLegend
      \else\gradbarsPrepareLegendAlt\tikz[baseline=0pt]{\gradbarsDrawLegend}\fi
    \fi
  }
\ExplSyntaxOff
\NewDocumentCommand\gradbarscolumn{m m}{%
  \gradbarsDefineColumn{#1}{#2}}
\ExplSyntaxOn
\cs_new_protected:Npn \gradbarsDefineColumn #1#2
  {
    \regex_match:nnTF {\A[A-Za-z]\Z} {#1}
      {\expandafter\newcolumntype\expandafter{#1}
        {>{\collectcell{\gradbar[#2]}}l<{\endcollectcell}}}
      {\PackageError{gradbars}{Column~name~must~be~one~ASCII~letter}
        {Choose~a~single~unused~letter,~for~example~G.}}
  }
\ExplSyntaxOff

% Data import, print patterns and metric direction (v0.0.2).
\def\gradbars@better{higher}
\def\gradbars@pattern{none}
\def\gradbars@patterncolor{black}
\def\gradbars@stackpatterns{none}
\newif\ifgradbars@print
\pgfkeys{/gradbars,
  print labels/.is if=gradbars@print,
  better/.is choice,
  better/higher/.code=\def\gradbars@better{higher},
  better/lower/.code=\def\gradbars@better{lower},
  pattern/.store in=\gradbars@pattern,
  pattern color/.store in=\gradbars@patterncolor,
  stack patterns/.store in=\gradbars@stackpatterns,
  print/.style={outline=false,print labels=true,theme=gray,color=white,track color=black!8,
    negative color=white,text color=black,segment text color=black,
    pattern=diagonal,pattern color=black,
    stack colors={white},stack patterns={diagonal,dots,crosshatch},
    threshold colors={black!10,black!25,black!45}}
}
\ExplSyntaxOn
\prop_const_from_keyval:Nn \c__gradbars_patterns_prop
  {none=none,diagonal=north~east~lines,reverse=north~west~lines,
   dots=dots,crosshatch=crosshatch,horizontal=horizontal~lines,vertical=vertical~lines}
\clist_new:N \l__gradbars_patterns_clist
\tl_new:N \l__gradbars_pattern_tl
\cs_new_protected:Npn \gradbarsPatternRect #1#2#3#4#5
  {
    \int_compare:nNnTF {#1} = {0}
      {\tl_set:No \l__gradbars_pattern_tl {\gradbars@pattern}}
      {\clist_set:No \l__gradbars_patterns_clist {\gradbars@stackpatterns}
       \tl_set:Nx \l__gradbars_pattern_tl
         {\clist_item:Nn \l__gradbars_patterns_clist
           {1+\int_mod:nn {#1-1}{\clist_count:N \l__gradbars_patterns_clist}}}}
    \gradbarsCategoryPattern{#1}
    \prop_get:NVNTF \c__gradbars_patterns_prop \l__gradbars_pattern_tl \l__gradbars_pattern_tl
      {\tl_if_eq:NnF \l__gradbars_pattern_tl {none}
        {\path[pattern=\l__gradbars_pattern_tl,pattern~color=\gradbars@patterncolor,draw=none]
          (#2,#3) rectangle (#4,#5);}}
      {\gradbarsFail{Unknown~pattern}}
  }
\cs_new_eq:NN \gradbarsValidateBeforePatterns \gradbarsValidateExtras
\cs_set_protected:Npn \gradbarsValidateExtras
  {
    \gradbarsValidateBeforePatterns
    \gradbarsValidatePatterns
  }
\cs_new_protected:Npn \gradbarsValidatePatterns
  {
    \clist_set:No \l__gradbars_patterns_clist {\gradbars@stackpatterns}
    \clist_if_empty:NT \l__gradbars_patterns_clist {\gradbarsFail{Stack~patterns~must~not~be~empty}}
    \clist_put_left:No \l__gradbars_patterns_clist {\gradbars@pattern}
    \clist_map_inline:Nn \l__gradbars_patterns_clist
      {\prop_if_in:NnF \c__gradbars_patterns_prop {##1}{\gradbarsFail{Unknown~pattern:~##1}}}
  }

% CSV is read as character data; fields are never executed as TeX.
\ior_new:N \l__gradbars_csv_ior
\prop_new:N \l__gradbars_csv_prop
\seq_new:N \l__gradbars_csv_fields_seq
\tl_new:N \l__gradbars_csv_field_tl
\tl_new:N \l__gradbars_csv_line_tl
\tl_new:N \l__gradbars_csv_value_tl
\tl_new:N \l__gradbars_csv_table_tl
\clist_new:N \l__gradbars_csv_missing_clist
\int_new:N \l__gradbars_csv_state_int
\int_new:N \l__gradbars_csv_row_int
\int_new:N \l__gradbars_csv_col_int
\int_new:N \l__gradbars_csv_width_int
\fp_new:N \l__gradbars_csv_min_fp
\fp_new:N \l__gradbars_csv_max_fp
\keys_define:nn {gradbars/csv}
  {missing .clist_set:N=\l__gradbars_csv_missing_clist}
\cs_generate_variant:Nn \prop_put:Nnn {Nxn,Nxx,NVx}
\cs_generate_variant:Nn \prop_get:NnN {cxN}
\cs_generate_variant:Nn \prop_if_in:NnTF {cxTF}
\cs_new_protected:Npn \gradbarsCSVField
  {\tl_trim_spaces:N \l__gradbars_csv_field_tl
   \seq_put_right:NV \l__gradbars_csv_fields_seq \l__gradbars_csv_field_tl
   \tl_clear:N \l__gradbars_csv_field_tl
   \int_zero:N \l__gradbars_csv_state_int}
\cs_new_protected:Npn \gradbarsCSVParse #1
  {
    \seq_clear:N \l__gradbars_csv_fields_seq
    \tl_clear:N \l__gradbars_csv_field_tl
    \int_zero:N \l__gradbars_csv_state_int
    % states: 0=unquoted, 1=quoted, 2=closing quote, 3=trailing space.
    \str_map_inline:nn {#1}
      {
        \int_case:nn {\l__gradbars_csv_state_int}
          {
            {0}{\str_case:nnF {##1}
              {{,}{\gradbarsCSVField}
               {"}{\tl_if_blank:VTF \l__gradbars_csv_field_tl
                    {\tl_clear:N \l__gradbars_csv_field_tl\int_set:Nn \l__gradbars_csv_state_int {1}}
                    {\gradbarsFail{Quote~inside~unquoted~CSV~field}}}}
              {\tl_put_right:Nn \l__gradbars_csv_field_tl {##1}}}
            {1}{\str_if_eq:nnTF {##1}{"}
              {\int_set:Nn \l__gradbars_csv_state_int {2}}
              {\tl_put_right:Nn \l__gradbars_csv_field_tl {##1}}}
            {2}{\str_case:nnF {##1}
              {{"}{\tl_put_right:Nn \l__gradbars_csv_field_tl {"}\int_set:Nn \l__gradbars_csv_state_int {1}}
               {,}{\gradbarsCSVField}}
              {\str_if_eq:nnTF {##1}{~}
                {\int_set:Nn \l__gradbars_csv_state_int {3}}
                {\gradbarsFail{Unexpected~text~after~CSV~quote}}}}
            {3}{\str_if_eq:nnTF {##1}{,}{\gradbarsCSVField}
              {\str_if_eq:nnF {##1}{~}{\gradbarsFail{Unexpected~text~after~CSV~quote}}}}
          }
      }
    \int_compare:nNnT {\l__gradbars_csv_state_int} = {1}
      {\gradbarsFail{Unclosed~CSV~quote;~multiline~fields~are~not~supported}}
    \gradbarsCSVField
  }
\cs_new_protected:Npn \gradbarsCSVLine #1
  {
    \tl_set:Nn \l__gradbars_csv_line_tl {#1}
    % pdfTeX reads UTF-8 as bytes; Unicode engines read a single BOM character.
    \sys_if_engine_pdftex:TF
      {\regex_replace_once:nnN {\A\x{EF}\x{BB}\x{BF}} {} \l__gradbars_csv_line_tl}
      {\regex_replace_once:nnN {\A\x{FEFF}} {} \l__gradbars_csv_line_tl}
    \tl_if_blank:VF \l__gradbars_csv_line_tl
      {
        \exp_args:NV \gradbarsCSVParse \l__gradbars_csv_line_tl
        \int_compare:nNnTF {\l__gradbars_csv_row_int} = {0}
          {
            \int_set:Nn \l__gradbars_csv_width_int {\seq_count:N \l__gradbars_csv_fields_seq}
            \int_zero:N \l__gradbars_csv_col_int
            \seq_map_inline:Nn \l__gradbars_csv_fields_seq
              {\int_incr:N \l__gradbars_csv_col_int
               \tl_if_blank:nT {##1}{\gradbarsFail{CSV~header~must~not~be~empty}}
               \prop_if_in:NnT \l__gradbars_csv_prop {col/##1}{\gradbarsFail{Duplicate~CSV~header:~##1}}
               \prop_put:Nnx \l__gradbars_csv_prop {col/##1}{\int_use:N \l__gradbars_csv_col_int}}
          }
          {
            \int_compare:nNnF {\seq_count:N \l__gradbars_csv_fields_seq} = {\l__gradbars_csv_width_int}
              {\gradbarsFail{CSV~row~has~wrong~field~count}}
            \int_zero:N \l__gradbars_csv_col_int
            \seq_map_inline:Nn \l__gradbars_csv_fields_seq
              {\int_incr:N \l__gradbars_csv_col_int
               \prop_put:Nxn \l__gradbars_csv_prop
                 {row/\int_use:N \l__gradbars_csv_row_int/\int_use:N \l__gradbars_csv_col_int}{##1}}
          }
        \int_incr:N \l__gradbars_csv_row_int
      }
  }
\NewDocumentCommand\gradbarsloadcsv{O{} m m}
  {
    \gradbars@validtrue
    \regex_match:nnF {\A[A-Za-z][A-Za-z0-9_\-]*\Z}{#2}
      {\gradbarsFail{CSV~dataset~name~must~start~with~an~ASCII~letter}}
    \clist_set:Nn \l__gradbars_csv_missing_clist {NA,N/A,null}
    \keys_set:nn {gradbars/csv}{#1}
    \prop_clear:N \l__gradbars_csv_prop
    \int_zero:N \l__gradbars_csv_row_int
    \file_if_exist:nTF {#3}
      {\ior_open:Nn \l__gradbars_csv_ior {#3}
       \ior_str_map_inline:Nn \l__gradbars_csv_ior {\gradbarsCSVLine{##1}}
       \ior_close:N \l__gradbars_csv_ior}
      {\gradbarsFail{CSV~file~not~found:~#3}}
    \int_compare:nNnF {\l__gradbars_csv_row_int} > {0}{\gradbarsFail{CSV~requires~a~header}}
    \ifgradbars@valid
      \prop_put:Nnx \l__gradbars_csv_prop {rows}{\int_eval:n {\l__gradbars_csv_row_int-1}}
      \prop_put:NnV \l__gradbars_csv_prop {missing}\l__gradbars_csv_missing_clist
      \prop_if_exist:cF {l__gradbars_csv_#2_prop}{\prop_new:c {l__gradbars_csv_#2_prop}}
      \prop_set_eq:cN {l__gradbars_csv_#2_prop}\l__gradbars_csv_prop
    \fi
  }
\cs_new_protected:Npn \gradbarsCSVColumn #1#2
  {
    \gradbars@validtrue
    \prop_if_exist:cTF {l__gradbars_csv_#1_prop}
      {\prop_if_in:cxTF {l__gradbars_csv_#1_prop}{col/\tl_to_str:n {#2}}
        {\prop_get:cxN {l__gradbars_csv_#1_prop}{col/\tl_to_str:n {#2}}\l_tmpa_tl
         \int_set:Nn \l__gradbars_csv_col_int {\l_tmpa_tl}}
        {\gradbarsFail{Unknown~CSV~column:~#2}}}
      {\gradbarsFail{Unknown~CSV~dataset:~#1}}
  }
\cs_new_protected:Npn \gradbarsCSVGet #1#2#3
  {
    \gradbarsCSVColumn{#1}{#3}
    \regex_match:nnF {\A[1-9][0-9]*\Z}{#2}{\gradbarsFail{CSV~row~must~be~a~positive~integer}}
    \ifgradbars@valid
      \prop_if_in:cxTF {l__gradbars_csv_#1_prop}{row/#2/\int_use:N \l__gradbars_csv_col_int}
        {\prop_get:cxN {l__gradbars_csv_#1_prop}{row/#2/\int_use:N \l__gradbars_csv_col_int}\l__gradbars_csv_value_tl}
        {\gradbarsFail{CSV~row~out~of~range}}
    \fi
  }
\cs_new_protected:Npn \gradbarsCSVNumber #1
  {
    \prop_get:cnN {l__gradbars_csv_#1_prop}{missing}\l_tmpa_tl
    \clist_set:NV \l__gradbars_csv_missing_clist \l_tmpa_tl
    \bool_set_false:N \l_tmpa_bool
    \tl_if_blank:VT \l__gradbars_csv_value_tl {\bool_set_true:N \l_tmpa_bool}
    \clist_map_inline:Nn \l__gradbars_csv_missing_clist
      {\str_if_eq:eeT {\l__gradbars_csv_value_tl}{\tl_to_str:n {##1}}{\bool_set_true:N \l_tmpa_bool}}
    \bool_if:NTF \l_tmpa_bool
      {\tl_set:Nn \l__gradbars_csv_value_tl {NA}}
      {\gradbarsCheckNumber{\l__gradbars_csv_value_tl}{CSV~numeric~cell}}
  }
\NewDocumentCommand\gradbarscsvcell{m m m}
  {\group_begin:\gradbarsCSVGet{#1}{#2}{#3}
   \ifgradbars@valid\tl_use:N \l__gradbars_csv_value_tl\fi\group_end:}
\NewDocumentCommand\gradbarcsv{O{} m m m}
  {\group_begin:\gradbarsCSVGet{#2}{#3}{#4}
   \ifgradbars@valid\gradbarsCSVNumber{#2}
     \ifgradbars@valid\gradbar[#1]{\l__gradbars_csv_value_tl}\fi\fi\group_end:}
\cs_new_protected:Npn \gradbarsCSVRange #1#2
  {
    \gradbarsCSVColumn{#1}{#2}
    \fp_zero:N \l__gradbars_csv_min_fp
    \fp_zero:N \l__gradbars_csv_max_fp
    \ifgradbars@valid
      \int_step_inline:nn {\prop_item:cn {l__gradbars_csv_#1_prop}{rows}}
        {\prop_get:cxN {l__gradbars_csv_#1_prop}{row/##1/\int_use:N \l__gradbars_csv_col_int}\l__gradbars_csv_value_tl
         \gradbarsCSVNumber{#1}
         \ifgradbars@valid
           \str_if_eq:VnF \l__gradbars_csv_value_tl {NA}
             {\fp_set:Nn \l__gradbars_csv_min_fp {min(\l__gradbars_csv_min_fp,\l__gradbars_csv_value_tl)}
              \fp_set:Nn \l__gradbars_csv_max_fp {max(\l__gradbars_csv_max_fp,\l__gradbars_csv_value_tl)}}
         \fi}
      % Keep a positive upper limit even for all-zero/all-missing/negative columns.
      \fp_compare:nNnT {\l__gradbars_csv_max_fp} = {0}{\fp_set:Nn \l__gradbars_csv_max_fp {1}}
    \fi
  }
\NewDocumentCommand\gradbarscsvstyle{m m m}
  {\gradbarsCSVRange{#2}{#3}
   \ifgradbars@valid
     \cs_set:cpx {gradbars@style@#1}
       {min=\fp_to_decimal:N \l__gradbars_csv_min_fp,max=\fp_to_decimal:N \l__gradbars_csv_max_fp}
   \fi}
\NewDocumentCommand\gradbarscsvtable{O{} m m m}
  {
    \group_begin:
    \gradbarsCSVRange{#2}{#4}
    \ifgradbars@valid
      \tl_set:Nx \l__gradbars_csv_table_tl
        {\exp_not:N \gradbarssetup{min=\fp_to_decimal:N \l__gradbars_csv_min_fp,
          max=\fp_to_decimal:N \l__gradbars_csv_max_fp}
         \exp_not:N \begin{tabular}{@{}ll@{}}\exp_not:N \hline
         \tl_to_str:n {#3}&\tl_to_str:n {#4}\exp_not:N \\\exp_not:N \hline}
      \int_step_inline:nn {\prop_item:cn {l__gradbars_csv_#2_prop}{rows}}
        {\gradbarsCSVGet{#2}{##1}{#3}
         \ifgradbars@valid
           \tl_put_right:Nx \l__gradbars_csv_table_tl
             {\exp_not:V \l__gradbars_csv_value_tl &
              \exp_not:N \gradbarcsv[\exp_not:n {#1}]{#2}{##1}{\exp_not:n {#4}}\exp_not:N \\}
         \fi}
      \tl_put_right:Nn \l__gradbars_csv_table_tl {\hline\end{tabular}}
      \ifgradbars@valid\tl_use:N \l__gradbars_csv_table_tl\fi
    \fi
    \group_end:
  }
\ExplSyntaxOff

% Compact comparison shapes and reusable visual presets, v0.0.2.
\newif\ifgradbars@compare
\newif\ifgradbars@rangemode
\newif\ifgradbars@referencemissing
\newif\ifgradbars@rangemissing
\newif\ifgradbars@outline
\def\gradbars@shape{bar}
\def\gradbars@reference{0}
\def\gradbars@comparecolor{black!20}
\def\gradbars@compareratio{.45}
\def\gradbars@rangelo{0}
\def\gradbars@rangehi{0}
\def\gradbars@rangepoint{}
\def\gradbars@markersize{2pt}
\def\gradbars@stemwidth{.6pt}
\def\gradbars@outlinewidth{.4pt}
\def\gradbars@rowpadding{0pt}
\pgfkeys{/gradbars,
  shape/.is choice,
  shape/bar/.code=\def\gradbars@shape{bar},
  shape/lollipop/.code=\def\gradbars@shape{lollipop},
  compare color/.store in=\gradbars@comparecolor,
  compare ratio/.store in=\gradbars@compareratio,
  range point/.store in=\gradbars@rangepoint,
  marker size/.store in=\gradbars@markersize,
  stem width/.store in=\gradbars@stemwidth,
  outline/.is if=gradbars@outline,outline/.default=true,
  outline width/.store in=\gradbars@outlinewidth,
  row padding/.store in=\gradbars@rowpadding,
  preset/.is choice,
  preset/paper/.style={theme=solid,shape=bar,outline=false,
    height=5pt,rounded=0pt,track color=black!4,label=outside,
    font=\small,label gap=.35em,row padding=.5pt,pattern=none,stack patterns={none},print labels=false},
  preset/report/.style={theme=solid,shape=bar,outline=false,
    height=12pt,rounded=3pt,track color=black!6,label=outside,
    font=\normalsize,label gap=.6em,row padding=2pt,target color=gradbarsInk,
    threshold colors={lightred,gold,lightgreen},pattern=none,stack patterns={none},print labels=false},
  preset/presentation/.style={theme=solid,shape=bar,outline=false,
    height=18pt,rounded=2pt,label=auto,text color=auto,
    font=\large,label gap=.7em,row padding=4pt,track color=black!10,
    pattern=none,stack patterns={none},print labels=false},
  preset/outline/.style={theme=gray,shape=bar,outline=true,
    height=7pt,rounded=0pt,track color=white,left color=black!80,
    compare color=black!35,label=outside,font=\small,
    outline width=.35pt,stem width=.4pt,label gap=.4em,row padding=1pt,
    pattern=none,stack patterns={none},print labels=false}
}
\def\gradbarsResolveShapeLengths{%
  \begingroup\font@name
  \edef\gradbars@next{\endgroup
    \def\noexpand\gradbars@markersize{\the\dimexpr\gradbars@markersize\relax}%
    \def\noexpand\gradbars@stemwidth{\the\dimexpr\gradbars@stemwidth\relax}%
    \def\noexpand\gradbars@outlinewidth{\the\dimexpr\gradbars@outlinewidth\relax}%
    \def\noexpand\gradbars@rowpadding{\the\dimexpr\gradbars@rowpadding\relax}}%
  \gradbars@next}
\NewDocumentCommand\gradcompare{O{} m m}{%
  \begingroup\gradbars@comparetrue
    \def\gradbars@reference{#2}%
    \gradbar[shape=bar,#1]{#3}%
  \endgroup}
\NewDocumentCommand\gradrange{O{} m m}{%
  \begingroup\gradbars@rangemodetrue
    \def\gradbars@rangelo{#2}\def\gradbars@rangehi{#3}%
    \gradbar[shape=bar,#1]{#3}%
  \endgroup}
\ExplSyntaxOn
\cs_new_eq:NN \gradbarsValidateBeforeShapes \gradbarsValidateExtras
\cs_set_protected:Npn \gradbarsValidateExtras
  {
    \gradbarsValidateBeforeShapes
    \clist_map_inline:nn {markersize,stemwidth,outlinewidth}
      {\dim_compare:nNnF {\use:c {gradbars@##1}} > {0pt}
        {\gradbarsFail{Marker~and~line~sizes~must~be~positive}}}
    \dim_compare:nNnT {\gradbars@rowpadding} < {0pt}
      {\gradbarsFail{Row~padding~must~be~nonnegative}}
    \ifgradbars@stack
      \str_if_eq:eeF {\gradbars@shape}{bar}{\gradbarsFail{Stacked~bars~require~shape=bar}}
    \fi
    \ifgradbars@compare
      \str_case_e:nnF {\gradbars@shape}{{bar}{}{dumbbell}{}}
        {\gradbarsFail{Comparison~requires~shape=bar~or~dumbbell}}
      \gradbarsCheckNumber{\gradbars@compareratio}{compare~ratio}
      \gradbars@referencemissingfalse
      \tl_set:Nx \l_tmpa_tl {\gradbars@reference}\tl_trim_spaces:N \l_tmpa_tl
      \tl_if_empty:NT \l_tmpa_tl {\gradbars@referencemissingtrue}
      \str_if_eq:VnT \l_tmpa_tl {NA}{\gradbars@referencemissingtrue}
      \ifgradbars@referencemissing\else\gradbarsCheckNumber{\gradbars@reference}{reference}\fi
      \ifgradbars@valid
        \fp_compare:nF {0<\gradbars@compareratio<1}{\gradbarsFail{Compare~ratio~must~lie~strictly~between~0~and~1}}
        \ifgradbars@referencemissing\else
          \fp_compare:nNnT {\gradbars@min} = {0}
            {\fp_compare:nNnT {\gradbars@reference} < {0}
              {\gradbarsFail{Negative~reference~requires~a~negative~min}}}
          \gradbarsRange{Reference}{\gradbars@reference}
        \fi
      \fi
    \fi
    \ifgradbars@rangemode
      \str_if_eq:eeF {\gradbars@shape}{bar}{\gradbarsFail{Range~requires~shape=bar}}
      \ifgradbars@error\gradbarsFail{Range~bars~do~not~accept~error~whiskers}\fi
      \clist_if_empty:NF \l__gradbars_thresholds_clist
        {\gradbarsFail{Range~bars~do~not~accept~thresholds}}
      \gradbars@rangemissingfalse
      \tl_set:Nx \l_tmpa_tl {\gradbars@rangelo}\tl_trim_spaces:N \l_tmpa_tl
      \tl_if_empty:NT \l_tmpa_tl {\gradbars@rangemissingtrue}
      \str_if_eq:VnT \l_tmpa_tl {NA}{\gradbars@rangemissingtrue}
      \ifgradbars@rangemissing
        \ifgradbars@missing\else\gradbarsFail{Both~range~endpoints~must~be~present~or~missing}\fi
      \else
        \ifgradbars@missing\gradbarsFail{Both~range~endpoints~must~be~present~or~missing}\fi
        \gradbarsCheckNumber{\gradbars@rangelo}{range~lower~endpoint}
      \fi
      \tl_if_empty:oF {\gradbars@rangepoint}{\gradbarsCheckNumber{\gradbars@rangepoint}{range~point}}
      \ifgradbars@valid
        \ifgradbars@missing
          \tl_if_empty:oF {\gradbars@rangepoint}{\gradbarsFail{Missing~range~cannot~have~a~point}}
        \else
          \fp_compare:nNnT {\gradbars@rangelo} > {\gradbars@rangehi}
            {\gradbarsFail{Range~lower~endpoint~must~not~exceed~upper}}
          \fp_compare:nNnT {\gradbars@min} = {0}
            {\fp_compare:nNnT {\gradbars@rangelo} < {0}
              {\gradbarsFail{Negative~range~requires~a~negative~min}}}
          \gradbarsRange{Range~lower~endpoint}{\gradbars@rangelo}
          \tl_if_empty:oF {\gradbars@rangepoint}
            {\fp_compare:nF {\gradbars@rangelo<=\gradbars@rangepoint<=\gradbars@rangehi}
              {\gradbarsFail{Range~point~must~lie~between~endpoints}}}
          \cs_set:Npx \gradbars@start {\gradbarsPosition{\gradbars@rangelo}}
          \cs_set:Npx \gradbars@stop {\gradbarsPosition{\gradbars@rangehi}}
        \fi
      \fi
    \else
      \tl_if_empty:oF {\gradbars@rangepoint}{\gradbarsFail{Range~point~requires~gradrange}}
    \fi
  }
% Format each endpoint independently using the ordinary number formatter.
\cs_new_eq:NN \gradbarsSingleLabel \gradbars@labelcontent
\cs_new_protected:Npn \gradbarsFormatEndpoint #1
  {\group_begin:\gradbars@missingfalse\def\gradbars@text{}
   \cs_set:Npx \gradbars@number {\fp_eval:n {#1}}
   \str_if_eq:eeT {\gradbars@format}{percent}
     {\cs_set:Npx \gradbars@number {\fp_eval:n {100*(#1)/\gradbars@max}}}
   \gradbarsSingleLabel\group_end:}
\cs_set_protected:Npn \gradbars@labelcontent
  {
    \tl_if_empty:oTF {\gradbars@text}
      {\ifgradbars@compare
         \gradbarsSingleLabel\,/\,
         \ifgradbars@referencemissing\gradbars@missingtext
         \else\gradbarsFormatEndpoint{\gradbars@reference}\fi
       \else\ifgradbars@rangemode
         \ifgradbars@missing\gradbars@missingtext
         \else[\gradbarsFormatEndpoint{\gradbars@rangelo},\,
               \gradbarsFormatEndpoint{\gradbars@rangehi}]\fi
       \else\gradbarsSingleLabel\fi\fi}
      {\gradbars@text}
  }
\cs_new_protected:Npn \gradbarsDrawTrack
  {
    \str_if_eq:eeTF {\gradbars@shape}{lollipop}
      {\draw[draw=\gradbars@track,line~width=\gradbars@stemwidth]
        (0,.5*\gradbars@height)--(\gradbars@width,.5*\gradbars@height);}
      {\ifgradbars@outline
        \path[rounded~corners=\gradbars@radius,fill=\gradbars@track,
          draw=gradbarsInk!35,line~width=\gradbars@outlinewidth]
          (0,0) rectangle (\gradbars@width,\gradbars@height);
       \else
        \path[rounded~corners=\gradbars@radius,fill=\gradbars@track,draw=none]
          (0,0) rectangle (\gradbars@width,\gradbars@height);
       \fi}
  }
\cs_new_protected:Npn \gradbarsDrawDot #1
  {
    \pgfmathsetlengthmacro\gradbars@dotradius{min(\gradbars@markersize,.5*\gradbars@height)}
    \ifgradbars@outline
      \path[fill=white,draw=\gradbars@left,line~width=\gradbars@stemwidth]
        (#1,.5*\gradbars@height) circle[radius=\gradbars@dotradius];
    \else
      \path[fill=\gradbars@left,draw=none]
        (#1,.5*\gradbars@height) circle[radius=\gradbars@dotradius];
    \fi
  }
\cs_new_protected:Npn \gradbarsDrawShape
  {
    \str_if_eq:eeTF {\gradbars@shape}{lollipop}
      {\ifgradbars@missing\else
         \draw[draw=\gradbars@left,line~width=\gradbars@stemwidth]
           (\gradbars@xzero,.5*\gradbars@height)--(\gradbars@xend,.5*\gradbars@height);
         \gradbarsDrawDot{\gradbars@xend}
       \fi}
      {
        \ifgradbars@compare
          \ifgradbars@referencemissing\else
            \pgfmathsetlengthmacro\gradbars@xref{\gradbarsPosition{\gradbars@reference}*(\gradbars@width)}
            \ifgradbars@outline
              \draw[draw=\gradbars@comparecolor,line~width=\gradbars@outlinewidth]
                (\gradbars@xzero,0) rectangle (\gradbars@xref,\gradbars@height);
            \else
              \path[rounded~corners=\gradbars@radius,fill=\gradbars@comparecolor,draw=none]
                (\gradbars@xzero,0) rectangle (\gradbars@xref,\gradbars@height);
            \fi
          \fi
          \begin{scope}[yshift={.5*(1-\gradbars@compareratio)*\gradbars@height}]
            \pgfmathsetlengthmacro\gradbars@height{\gradbars@compareratio*\gradbars@height}
            \pgfmathsetlengthmacro\gradbars@radius{min(\gradbars@radius,.5*\gradbars@height)}
            \gradbarsDrawBarBody
          \end{scope}
        \else
          \gradbarsDrawBarBody
          \ifgradbars@rangemode\ifgradbars@missing\else
            \draw[draw=\gradbars@left,line~width=\gradbars@stemwidth]
              (\gradbars@xstart,0)--(\gradbars@xstart,\gradbars@height)
              (\gradbars@xstop,0)--(\gradbars@xstop,\gradbars@height);
            \tl_if_empty:oF {\gradbars@rangepoint}
              {\pgfmathsetlengthmacro\gradbars@xpoint{\gradbarsPosition{\gradbars@rangepoint}*(\gradbars@width)}
               \pgfmathsetlengthmacro\gradbars@dotradius{min(\gradbars@markersize,.5*\gradbars@height)}
               \gradbarsDrawMarker{\gradbars@xpoint}{.5*\gradbars@height}{gradbarsInk}{hollow}}
          \fi\fi
        \fi
      }
  }
\ExplSyntaxOff
\def\gradbarsDrawBarBody{%
  \ifdim\gradbars@xstop>\gradbars@xstart\relax
    \pgfmathsetlengthmacro\gradbars@fillradius{min(\gradbars@radius,.5*(\gradbars@xstop-\gradbars@xstart))}%
    \begin{scope}
      \clip[rounded corners=\gradbars@radius]
        (0,0) rectangle (\gradbars@width,\gradbars@height);
      \ifgradbars@stack
        \clip[rounded corners=\gradbars@fillradius]
          (\gradbars@xstart,0) rectangle (\gradbars@xstop,\gradbars@height);
        \gradbarsDrawStack
      \else\ifgradbars@outline
        \draw[rounded corners=\gradbars@fillradius,draw=\gradbars@left,line width=\gradbars@outlinewidth]
          (\gradbars@xstart,0) rectangle (\gradbars@xstop,\gradbars@height);
      \else\ifgradbars@gradient
        \path[rounded corners=\gradbars@fillradius,shade,
          left color=\gradbars@left,right color=\gradbars@right]
          (\gradbars@xstart,0) rectangle (\gradbars@xstop,\gradbars@height);
      \else
        \path[rounded corners=\gradbars@fillradius,fill=\gradbars@left,draw=none]
          (\gradbars@xstart,0) rectangle (\gradbars@xstop,\gradbars@height);
      \fi\fi\fi
      \ifgradbars@stack\else\ifgradbars@outline\else
        \clip[rounded corners=\gradbars@fillradius]
          (\gradbars@xstart,0) rectangle (\gradbars@xstop,\gradbars@height);
        \gradbarsPatternRect{0}{\gradbars@xstart}{0}{\gradbars@xstop}{\gradbars@height}
      \fi
      \fi
    \end{scope}
  \fi
}

% v0.0.3: comparison, signed compositions, series and semantic palettes.
\definecolor{gradbarsOrange}{HTML}{D55E00}
\definecolor{gradbarsPurple}{HTML}{AA4499}
\definecolor{gradbarsCyan}{HTML}{56B4E9}
\definecolor{gradbarsGreen}{HTML}{009E73}
\def\gradbars@comparelabel{values}
\def\gradbars@stacktotals{net}
\def\gradbars@qualitytarget{}
\def\gradbars@qualityrange{}
\def\gradbars@sparkrange{auto}
\def\gradbars@sparkpoints{extrema}
\def\gradbars@sparkhigh{gradbarsTeal}
\def\gradbars@sparklow{gradbarsOrange}
\newif\ifgradbars@spark
\pgfkeys{/gradbars,
  shape/dumbbell/.code=\def\gradbars@shape{dumbbell},
  shape/sparkline/.code=\def\gradbars@shape{sparkline},
  compare label/.is choice,
  compare label/values/.code=\def\gradbars@comparelabel{values},
  compare label/delta/.code=\def\gradbars@comparelabel{delta},
  stack totals/.is choice,
  stack totals/net/.code=\def\gradbars@stacktotals{net},
  stack totals/separate/.code=\def\gradbars@stacktotals{separate},
  better/target/.code=\def\gradbars@better{target},
  better/interval/.code=\def\gradbars@better{interval},
  quality target/.store in=\gradbars@qualitytarget,
  quality range/.store in=\gradbars@qualityrange,
  spark range/.is choice,
  spark range/auto/.code=\def\gradbars@sparkrange{auto},
  spark range/fixed/.code=\def\gradbars@sparkrange{fixed},
  spark points/.is choice,
  spark points/extrema/.code=\def\gradbars@sparkpoints{extrema},
  spark points/all/.code=\def\gradbars@sparkpoints{all},
  spark points/last/.code=\def\gradbars@sparkpoints{last},
  spark points/none/.code=\def\gradbars@sparkpoints{none},
  spark high color/.store in=\gradbars@sparkhigh,
  spark low color/.store in=\gradbars@sparklow,
  palette/.is choice,
  palette/categorical/.style={stack colors={gradbarsBlue,gradbarsOrange,gradbarsGreen,gradbarsPurple,gradbarsCyan,gold},
    stack patterns={none},compare color=gradbarsOrange,negative color=gradbarsOrange},
  palette/sequential/.style={stack colors={gradbarsBlue!20,gradbarsBlue!40,gradbarsBlue!60,gradbarsBlue!80,gradbarsBlue},
    stack patterns={none}},
  palette/diverging/.style={stack colors={gradbarsOrange!85,gradbarsOrange!40,black!12,gradbarsTeal!40,gradbarsTeal!85},
    threshold colors={gradbarsOrange,gold,gradbarsTeal},negative color=gradbarsOrange,
    compare color=gradbarsOrange,left color=gradbarsTeal,right color=gradbarsTeal!35,stack patterns={none}},
  palette/mono/.style={print,stack colors={white,black!10,black!20},
    stack patterns={diagonal,dots,crosshatch},compare color=black!45,
    spark high color=black,spark low color=black},
}
\NewDocumentCommand\graddumbbell{O{} m m}{%
  \gradcompare[#1,shape=dumbbell]{#2}{#3}}
\ExplSyntaxOn
\fp_new:N \l__gradbars_positive_fp
\fp_new:N \l__gradbars_negative_fp
\cs_new_protected:Npn \gradbarsStackReset
  {\fp_zero:N \l__gradbars_positive_fp\fp_zero:N \l__gradbars_negative_fp}
\cs_new_protected:Npn \gradbarsStackStep #1
  {
    \fp_compare:nNnTF {#1} < {0}
      {\fp_set_eq:NN \l__gradbars_sum_fp \l__gradbars_negative_fp
       \fp_add:Nn \l__gradbars_negative_fp {#1}
       \fp_set_eq:NN \l__gradbars_before_fp \l__gradbars_negative_fp}
      {\fp_set_eq:NN \l__gradbars_before_fp \l__gradbars_positive_fp
       \fp_add:Nn \l__gradbars_positive_fp {#1}
       \fp_set_eq:NN \l__gradbars_sum_fp \l__gradbars_positive_fp}
  }
% Category identity is scoped and keyed by literal names, independent of order.
\prop_new:N \l__gradbars_category_colors_prop
\prop_new:N \l__gradbars_category_patterns_prop
\NewDocumentCommand\gradbarscategory{m m m}
  {
    \tl_if_blank:nTF {#1}{\gradbarsFail{Category~name~must~not~be~empty}}
      {\prop_if_in:NnTF \c__gradbars_patterns_prop {#3}
        {\prop_put:Nnn \l__gradbars_category_colors_prop {#1}{#2}
         \prop_put:Nnn \l__gradbars_category_patterns_prop {#1}{#3}}
        {\gradbarsFail{Unknown~category~pattern}}}
  }
\cs_new_protected:Npn \gradbarsCategoryColor #1
  {
    \cs_set:Npx \gradbars@segmentcolor
      {\clist_item:Nn \l__gradbars_stackcolors_clist
        {1+\int_mod:nn {#1-1}{\clist_count:N \l__gradbars_stackcolors_clist}}}
    \tl_set:Nx \l_tmpa_tl {\clist_item:Nn \l__gradbars_names_clist {#1}}
    \ifgradbars@print\else
      \prop_get:NVNT \l__gradbars_category_colors_prop \l_tmpa_tl \l_tmpb_tl
        {\cs_set:Npx \gradbars@segmentcolor {\exp_not:V \l_tmpb_tl}}
    \fi
  }
\cs_new_protected:Npn \gradbarsCategoryPattern #1
  {
    \int_compare:nNnT {#1} > {0}
      {\tl_set:Nx \l_tmpa_tl {\clist_item:Nn \l__gradbars_names_clist {#1}}
       \prop_get:NVNT \l__gradbars_category_patterns_prop \l_tmpa_tl \l_tmpb_tl
         {\tl_set_eq:NN \l__gradbars_pattern_tl \l_tmpb_tl}}
  }
\fp_new:N \l__gradbars_distance_fp
\clist_new:N \l__gradbars_quality_clist
\cs_new_protected:Npn \gradbarsQuality
  {
    \str_case_e:nn {\gradbars@better}
      {{target}{\gradbarsCheckNumber{\gradbars@qualitytarget}{quality~target}}
       {interval}{\clist_set:Nx \l__gradbars_quality_clist {\gradbars@qualityrange}
         \tl_set:Nx \l_tmpa_tl {\gradbars@qualityrange}
         \regex_match:nVT {\A\s*,|,\s*,|,\s*\Z} \l_tmpa_tl
           {\gradbarsFail{Quality~range~requires~two~endpoints}}
         \int_compare:nNnF {\clist_count:N \l__gradbars_quality_clist} = {2}
           {\gradbarsFail{Quality~range~requires~two~endpoints}}
         \clist_map_inline:Nn \l__gradbars_quality_clist
           {\gradbarsCheckNumber{##1}{quality~endpoint}}}}
    \clist_if_empty:NT \l__gradbars_thresholds_clist
      {\gradbarsFail{Target~and~interval~quality~require~distance~thresholds}}
    \ifgradbars@stack\gradbarsFail{Stack~does~not~support~quality~evaluation}\fi
    \ifgradbars@rangemode\gradbarsFail{Range~does~not~support~quality~evaluation}\fi
    \ifgradbars@valid
      \fp_compare:nNnT {\clist_item:Nn \l__gradbars_thresholds_clist {1}} < {0}
        {\gradbarsFail{Distance~thresholds~must~be~nonnegative}}
      \str_if_eq:eeTF {\gradbars@better}{target}
        {\fp_set:Nn \l__gradbars_distance_fp {abs(\gradbars@value-\gradbars@qualitytarget)}}
        {\fp_compare:nNnT {\clist_item:Nn \l__gradbars_quality_clist {1}}
           > {\clist_item:Nn \l__gradbars_quality_clist {2}}
           {\gradbarsFail{Quality~range~must~be~ordered}}
         \fp_set:Nn \l__gradbars_distance_fp
           {max(0,\clist_item:Nn \l__gradbars_quality_clist {1}-\gradbars@value,
             \gradbars@value-\clist_item:Nn \l__gradbars_quality_clist {2})}}
      \int_set:Nn \l_tmpa_int {1}
      \fp_compare:nT {\l__gradbars_distance_fp <= \clist_item:Nn \l__gradbars_thresholds_clist {2}}
        {\int_set:Nn \l_tmpa_int {2}}
      \fp_compare:nT {\l__gradbars_distance_fp <= \clist_item:Nn \l__gradbars_thresholds_clist {1}}
        {\int_set:Nn \l_tmpa_int {3}}
      \cs_set:Npx \gradbars@left {\clist_item:Nn \l__gradbars_colors_clist {\l_tmpa_int}}
      \gradbars@gradientfalse
    \fi
  }
\cs_new_eq:NN \gradbarsValidateBeforeThree \gradbarsValidateExtras
\cs_set_protected:Npn \gradbarsValidateExtras
  {
    \gradbarsValidateBeforeThree
    \str_case_e:nn {\gradbars@better}{{target}{\gradbarsQuality}{interval}{\gradbarsQuality}}
    \str_if_eq:eeT {\gradbars@shape}{dumbbell}
      {\ifgradbars@compare\else\gradbarsFail{Dumbbell~requires~graddumbbell}\fi}
    \str_if_eq:eeT {\gradbars@shape}{sparkline}
      {\ifgradbars@spark\else\gradbarsFail{Sparkline~requires~gradspark}\fi}
    \ifgradbars@spark
      \str_if_eq:eeF {\gradbars@shape}{sparkline}{\gradbarsFail{Spark~requires~shape=sparkline}}
      \ifgradbars@error\gradbarsFail{Spark~does~not~support~error~whiskers}\fi
      \tl_if_empty:oF {\gradbars@target}{\gradbarsFail{Spark~does~not~support~bar~targets}}
      \tl_if_empty:oF {\gradbars@band}{\gradbarsFail{Spark~does~not~support~bar~bands}}
      \tl_if_empty:oF {\gradbars@thresholds}{\gradbarsFail{Spark~does~not~support~thresholds}}
      \gradbarsValidateSpark
    \fi
  }
\cs_new_eq:NN \gradbarsLabelBeforeThree \gradbars@labelcontent
\cs_set_protected:Npn \gradbars@labelcontent
  {
    \tl_if_empty:oTF {\gradbars@text}
      {\ifgradbars@compare
        \str_if_eq:eeTF {\gradbars@comparelabel}{delta}
          {\ifgradbars@missing\gradbars@missingtext\else
             \ifgradbars@referencemissing\gradbars@missingtext\else
               $\Delta$\,\gradbarsFormatEndpoint{\gradbars@value-\gradbars@reference}\fi\fi}
          {\gradbarsLabelBeforeThree}
       \else\ifgradbars@stack
         \str_if_eq:eeTF {\gradbars@stacktotals}{separate}
           {+\gradbarsFormatEndpoint{\gradbars@positive}\,/\,\gradbarsFormatEndpoint{\gradbars@negative}}
           {\gradbarsLabelBeforeThree}
       \else\gradbarsLabelBeforeThree\fi\fi}
      {\gradbars@text}
  }
\cs_new_eq:NN \gradbarsShapeBeforeThree \gradbarsDrawShape
\cs_set_protected:Npn \gradbarsDrawShape
  {
    \str_case_e:nnF {\gradbars@shape}
      {{dumbbell}{\gradbarsDrawDumbbell}{sparkline}{\gradbarsDrawSpark}}
      {\gradbarsShapeBeforeThree}
  }
\cs_new_protected:Npn \gradbarsDrawDumbbell
  {
    \gradbarsMarkerBounds
    \ifgradbars@referencemissing\else
      \pgfmathsetlengthmacro\gradbars@xref{\gradbarsPosition{\gradbars@reference}*\gradbars@width}
      \ifgradbars@missing\else
        \draw[draw=\gradbars@left,line~width=\gradbars@stemwidth]
          (\gradbars@xref,.5*\gradbars@height)--(\gradbars@xend,.5*\gradbars@height);
      \fi
      \group_begin:\let\gradbars@left\gradbars@comparecolor
        \gradbars@outlinetrue\gradbarsDrawDot{\gradbars@xref}\group_end:
    \fi
    \ifgradbars@missing\else\gradbarsDrawDot{\gradbars@xend}\fi
  }
\seq_new:N \l__gradbars_series_seq
\seq_new:N \l__gradbars_series_clean_seq
\fp_new:N \l__gradbars_series_min_fp
\fp_new:N \l__gradbars_series_max_fp
\fp_new:N \l__gradbars_series_low_fp
\fp_new:N \l__gradbars_series_high_fp
\bool_new:N \l__gradbars_series_any_bool
\bool_new:N \l__gradbars_series_prev_bool
\bool_new:N \l__gradbars_series_mark_bool
\int_new:N \l__gradbars_series_index_int
\cs_new_protected:Npn \gradbarsParseSpark #1
  {
    \fp_zero:N \l__gradbars_series_min_fp
    \fp_zero:N \l__gradbars_series_max_fp
    \seq_set_split:Nnn \l__gradbars_series_seq {,}{#1}
    \seq_clear:N \l__gradbars_series_clean_seq
    \bool_set_false:N \l__gradbars_series_any_bool
    \seq_map_inline:Nn \l__gradbars_series_seq
      {\tl_set:Nx \l_tmpa_tl {##1}\tl_trim_spaces:N \l_tmpa_tl
       \tl_if_empty:NT \l_tmpa_tl {\tl_set:Nn \l_tmpa_tl {NA}}
       \str_if_eq:VnF \l_tmpa_tl {NA}
         {\gradbarsCheckNumber{\l_tmpa_tl}{spark~observation}
          \ifgradbars@valid
            \bool_if:NTF \l__gradbars_series_any_bool
              {\fp_set:Nn \l__gradbars_series_min_fp {min(\l__gradbars_series_min_fp,\l_tmpa_tl)}
               \fp_set:Nn \l__gradbars_series_max_fp {max(\l__gradbars_series_max_fp,\l_tmpa_tl)}}
              {\fp_set:Nn \l__gradbars_series_min_fp {\l_tmpa_tl}
               \fp_set:Nn \l__gradbars_series_max_fp {\l_tmpa_tl}
               \bool_set_true:N \l__gradbars_series_any_bool}
          \fi}
       \seq_put_right:NV \l__gradbars_series_clean_seq \l_tmpa_tl}
    \seq_set_eq:NN \l__gradbars_series_seq \l__gradbars_series_clean_seq
    \cs_set:Npx \gradbars@sparklast {\seq_item:Nn \l__gradbars_series_seq {-1}}
  }
\NewDocumentCommand\gradspark{O{} m}
  {\group_begin:\gradbars@validtrue\gradbarsParseSpark{#2}
   \ifgradbars@valid
     \gradbars@sparktrue
     \gradbar[height=4ex,#1,shape=sparkline]{\gradbars@sparklast}
   \fi\group_end:}
\cs_new_protected:Npn \gradbarsValidateSpark
  {
    \str_if_eq:eeTF {\gradbars@sparkrange}{fixed}
      {\fp_set:Nn \l__gradbars_series_low_fp {\gradbars@min}
       \fp_set:Nn \l__gradbars_series_high_fp {\gradbars@max}
       \seq_map_inline:Nn \l__gradbars_series_seq
         {\str_if_eq:nnF {##1}{NA}{\gradbarsRange{Spark~observation}{##1}}}}
      {\fp_set_eq:NN \l__gradbars_series_low_fp \l__gradbars_series_min_fp
       \fp_set_eq:NN \l__gradbars_series_high_fp \l__gradbars_series_max_fp}
  }
\cs_new_protected:Npn \gradbarsDrawSpark
  {
    \gradbarsMarkerBounds
    \bool_set_false:N \l__gradbars_series_prev_bool
    \int_zero:N \l__gradbars_series_index_int
    \seq_map_inline:Nn \l__gradbars_series_seq
      {\int_incr:N \l__gradbars_series_index_int
       \str_if_eq:nnTF {##1}{NA}
         {\bool_set_false:N \l__gradbars_series_prev_bool}
         {\pgfmathsetlengthmacro\gradbars@sparkx
            {\fp_eval:n {\seq_count:N \l__gradbars_series_seq=1 ? .5 :
              (\l__gradbars_series_index_int-1)/max(1,\seq_count:N \l__gradbars_series_seq-1)}*\gradbars@width}
          \fp_compare:nNnTF {\l__gradbars_series_low_fp} = {\l__gradbars_series_high_fp}
            {\pgfmathsetlengthmacro\gradbars@sparky{.5*\gradbars@height}}
            {\pgfmathsetlengthmacro\gradbars@sparky
              {\fp_eval:n {max(0,min(1,(##1-\l__gradbars_series_low_fp)/
                (\l__gradbars_series_high_fp-\l__gradbars_series_low_fp)))}*\gradbars@height}}
          \bool_if:NT \l__gradbars_series_prev_bool
            {\draw[draw=\gradbars@left,line~width=\gradbars@stemwidth]
              (\gradbars@sparkprevx,\gradbars@sparkprevy)--(\gradbars@sparkx,\gradbars@sparky);}
          \bool_set_false:N \l__gradbars_series_mark_bool
          \let\gradbars@pointcolor\gradbars@left
          \str_case_e:nn {\gradbars@sparkpoints}
            {{all}{\bool_set_true:N \l__gradbars_series_mark_bool}
             {extrema}{\fp_compare:nNnT {##1} = {\l__gradbars_series_min_fp}
                {\bool_set_true:N \l__gradbars_series_mark_bool\let\gradbars@pointcolor\gradbars@sparklow}
               \fp_compare:nNnT {##1} = {\l__gradbars_series_max_fp}
                {\bool_set_true:N \l__gradbars_series_mark_bool\let\gradbars@pointcolor\gradbars@sparkhigh}}}
          \str_if_eq:eeF {\gradbars@sparkpoints}{none}
            {\int_compare:nNnT {\l__gradbars_series_index_int} = {\seq_count:N \l__gradbars_series_seq}
               {\bool_set_true:N \l__gradbars_series_mark_bool}}
          \bool_if:NT \l__gradbars_series_mark_bool
            {\gradbarsDrawMarker{\gradbars@sparkx}{\gradbars@sparky}
               {\gradbars@pointcolor}{\gradbars@markerfill}}
          \let\gradbars@sparkprevx\gradbars@sparkx\let\gradbars@sparkprevy\gradbars@sparky
          \bool_set_true:N \l__gradbars_series_prev_bool}}
  }
% Auto series scaling applies to the y axis; it must not inherit bar clipping.
\cs_new_eq:NN \gradbarsValidateBeforeSpark \gradbarsValidate
\cs_set_protected:Npn \gradbarsValidate
  {
    \ifgradbars@spark
      \str_if_eq:eeT {\gradbars@sparkrange}{auto}
        {\cs_set:Npx \gradbars@min {\fp_eval:n {min(0,\l__gradbars_series_min_fp)}}
         \cs_set:Npx \gradbars@max {\fp_eval:n {max(1,\l__gradbars_series_max_fp)}}
         \str_if_eq:eeT {\gradbars@format}{percent}
           {\gradbarsFail{Auto~spark~range~does~not~support~percent~format}}}
      \str_if_eq:eeF {\gradbars@label}{none}{\def\gradbars@label{outside}}
    \fi
    \let\gradbars@sparkbasecolor\gradbars@left
    \gradbarsValidateBeforeSpark
    \ifgradbars@spark\let\gradbars@left\gradbars@sparkbasecolor\fi
  }
\cs_new_protected:Npn \gradbarsMarkerBounds
  {\path
     (-\gradbars@markersize,-\gradbars@markersize) rectangle
     ([xshift=\gradbars@markersize,yshift=\gradbars@markersize]\gradbars@width,\gradbars@height);}
\cs_new_eq:NN \gradbarsTrackBeforeThree \gradbarsDrawTrack
\cs_set_protected:Npn \gradbarsDrawTrack
  {\group_begin:
   \str_if_eq:eeT {\gradbars@shape}{dumbbell}{\def\gradbars@shape{lollipop}}
   \gradbarsTrackBeforeThree\group_end:}
\ExplSyntaxOff
% Reference records: {value/color/line style/name}, names may be empty.
\def\gradbars@references{}
\def\gradbars@bulletbands{}
\newif\ifgradbars@bullet
\pgfkeys{/gradbars/.cd,
  references/.store in=\gradbars@references,
  bullet bands/.store in=\gradbars@bulletbands}
\NewDocumentCommand\gradbullet{O{} m}{%
  \begingroup\gradbars@bullettrue
  \gradbar[height=12pt,fill=solid,label=outside,#1,shape=bar]{#2}%
  \endgroup}
\ExplSyntaxOn
\seq_new:N \l__gradbars_record_seq
\clist_new:N \l__gradbars_refs_clist
\clist_new:N \l__gradbars_bands_clist
\fp_new:N \l__gradbars_band_start_fp
\tl_new:N \l__gradbars_record_color_tl
\tl_new:N \l__gradbars_record_style_tl
\tl_new:N \l__gradbars_record_name_tl
\cs_new_protected:Npn \gradbarsRecord #1#2
  {\seq_set_split:Nnn \l__gradbars_record_seq {/}{#1}
   \int_compare:nNnF {\seq_count:N \l__gradbars_record_seq} = {#2}
     {\gradbarsFail{Wrong~number~of~fields~in~reference,~band~or~interval~record}}}
\cs_new_eq:NN \gradbarsValidateBeforeReferences \gradbarsValidateExtras
\cs_set_protected:Npn \gradbarsValidateExtras
  {
    \gradbarsValidateBeforeReferences
    \clist_set:No \l__gradbars_refs_clist {\gradbars@references}
    \clist_set:No \l__gradbars_bands_clist {\gradbars@bulletbands}
    \ifgradbars@spark
      \clist_if_empty:NF \l__gradbars_refs_clist
        {\gradbarsFail{Spark~does~not~support~references}}
    \fi
    \clist_map_inline:Nn \l__gradbars_refs_clist
      {\gradbarsRecord{##1}{4}
       \ifgradbars@valid
         \gradbarsCheckNumber{\seq_item:Nn \l__gradbars_record_seq {1}}{reference}
         \ifgradbars@valid
           \fp_compare:nT
             {\seq_item:Nn \l__gradbars_record_seq {1}<\gradbars@min ||
              \seq_item:Nn \l__gradbars_record_seq {1}>\gradbars@max}
             {\gradbarsFail{Reference~must~lie~within~range}}
           \tl_set:Nx \l_tmpa_tl {\seq_item:Nn \l__gradbars_record_seq {3}}
           \exp_args:NnV \clist_if_in:nnF {solid,dashed,dotted} \l_tmpa_tl
             {\gradbarsFail{Reference~line~style~must~be~solid,~dashed~or~dotted}}
         \fi
       \fi}
    \ifgradbars@bullet
      \clist_if_empty:NT \l__gradbars_bands_clist
        {\gradbarsFail{Bullet~requires~explicit~bullet~bands}}
    \else
      \clist_if_empty:NF \l__gradbars_bands_clist
        {\gradbarsFail{Bullet~bands~require~gradbullet}}
    \fi
    \ifgradbars@valid
      \fp_set:Nn \l__gradbars_band_start_fp {\gradbars@min}
      \clist_map_inline:Nn \l__gradbars_bands_clist
        {\gradbarsRecord{##1}{2}
         \ifgradbars@valid
           \gradbarsCheckNumber{\seq_item:Nn \l__gradbars_record_seq {1}}{band~endpoint}
           \ifgradbars@valid
             \fp_compare:nT
               {\seq_item:Nn \l__gradbars_record_seq {1}<=\l__gradbars_band_start_fp ||
                \seq_item:Nn \l__gradbars_record_seq {1}>\gradbars@max}
               {\gradbarsFail{Bullet~endpoints~must~increase~within~range}}
             \fp_set:Nn \l__gradbars_band_start_fp {\seq_item:Nn \l__gradbars_record_seq {1}}
           \fi
         \fi}
      \ifgradbars@bullet\ifgradbars@valid
        \fp_compare:nNnF {\l__gradbars_band_start_fp} = {\gradbars@max}
          {\gradbarsFail{Last~bullet~endpoint~must~equal~max}}
      \fi\fi
    \fi
  }
\cs_new_protected:Npn \gradbarsDrawReferences
  {\int_zero:N \l_tmpa_int
   \clist_map_inline:Nn \l__gradbars_refs_clist
     {\seq_set_split:Nnn \l__gradbars_record_seq {/}{##1}
      \tl_set:Nx \l__gradbars_record_color_tl {\seq_item:Nn \l__gradbars_record_seq {2}}
      \tl_set:Nx \l__gradbars_record_style_tl {\seq_item:Nn \l__gradbars_record_seq {3}}
      \tl_set:Nx \l__gradbars_record_name_tl {\seq_item:Nn \l__gradbars_record_seq {4}}
      \pgfmathsetlengthmacro\gradbars@rx
        {\gradbarsPosition{\seq_item:Nn \l__gradbars_record_seq {1}}*(\gradbars@width)}
      \draw[draw=\l__gradbars_record_color_tl,line~width=.6pt,
        \l__gradbars_record_style_tl]
        (\gradbars@rx,-1pt)--(\gradbars@rx,{\gradbars@height+1pt});
      \tl_if_blank:VF \l__gradbars_record_name_tl
        {\node[anchor=south,inner~sep=1pt,font=\scriptsize,
           text=\l__gradbars_record_color_tl]
           at (\gradbars@rx,{\gradbars@height+2pt+10pt*\int_mod:nn{\l_tmpa_int}{2}})
           {\l__gradbars_record_name_tl};}
      \int_incr:N \l_tmpa_int}}
\cs_new_eq:NN \gradbarsBandBeforeBullet \gradbarsDrawBand
\cs_set_protected:Npn \gradbarsDrawBand
  {\ifgradbars@bullet
     \fp_set:Nn \l__gradbars_band_start_fp {\gradbars@min}
     \clist_map_inline:Nn \l__gradbars_bands_clist
       {\seq_set_split:Nnn \l__gradbars_record_seq {/}{##1}
        \tl_set:Nx \l__gradbars_record_color_tl {\seq_item:Nn \l__gradbars_record_seq {2}}
        \pgfmathsetlengthmacro\gradbars@bx
          {\gradbarsPosition{\l__gradbars_band_start_fp}*(\gradbars@width)}
        \pgfmathsetlengthmacro\gradbars@ex
          {\gradbarsPosition{\seq_item:Nn \l__gradbars_record_seq {1}}*(\gradbars@width)}
        \path[fill=\l__gradbars_record_color_tl,draw=none]
          (\gradbars@bx,0) rectangle (\gradbars@ex,\gradbars@height);
        \fp_set:Nn \l__gradbars_band_start_fp {\seq_item:Nn \l__gradbars_record_seq {1}}}
   \fi\gradbarsBandBeforeBullet}
\cs_new_eq:NN \gradbarsShapeBeforeBullet \gradbarsDrawShape
\cs_set_protected:Npn \gradbarsDrawShape
  {\ifgradbars@bullet
     \begin{scope}[yshift=.3*\gradbars@height,yscale=.4]
       \gradbarsShapeBeforeBullet
     \end{scope}
   \else\gradbarsShapeBeforeBullet\fi}
% Each interval record has name / lower / point (or NA) / upper / color.
\tl_new:N \l__gradbars_interval_rows_tl
\cs_new_protected:Npn \gradbarsIntervalRow #1#2#3#4#5
  {#1 & \gradrange[color=#5,range~point=#3]{#2}{#4}\\}
\NewDocumentCommand\gradintervals{O{} m}
  {\group_begin:
   \pgfkeys{/gradbars,#1}
   \gradbars@validtrue
   \tl_clear:N \l__gradbars_interval_rows_tl
   \clist_if_empty:nT {#2}{\gradbarsFail{Interval~comparison~requires~records}}
   \clist_map_inline:nn {#2}
     {\gradbarsRecord{##1}{5}
      \ifgradbars@valid
        \tl_set:Nx \l__gradbars_record_name_tl {\seq_item:Nn \l__gradbars_record_seq {1}}
        \tl_set:Nx \l_tmpa_tl {\seq_item:Nn \l__gradbars_record_seq {3}}
        \str_if_eq:VnT \l_tmpa_tl {NA}{\tl_clear:N \l_tmpa_tl}
        \tl_put_right:Nx \l__gradbars_interval_rows_tl
          {\exp_not:N \gradbarsIntervalRow
            {\exp_not:V \l__gradbars_record_name_tl}
            {\seq_item:Nn \l__gradbars_record_seq {2}}
            {\exp_not:V \l_tmpa_tl}
            {\seq_item:Nn \l__gradbars_record_seq {4}}
            {\seq_item:Nn \l__gradbars_record_seq {5}}}
      \fi}
   \ifgradbars@valid
     \begin{tabular}{@{}l@{\hspace{.7em}}l@{}}
       \l__gradbars_interval_rows_tl
     \end{tabular}
   \fi\group_end:}
\ExplSyntaxOff
% v0.0.4: overflow indicators, comparison annotations and marker shapes.
\def\gradbars@overflowmark{none}
\def\gradbars@markershape{circle}
\def\gradbars@markerfill{solid}
\def\gradbars@change{none}
\pgfkeys{/gradbars/.cd,
 overflow mark/.is choice,
 overflow mark/none/.code=\def\gradbars@overflowmark{none},
 overflow mark/arrow/.code=\def\gradbars@overflowmark{arrow},
 overflow mark/slash/.code=\def\gradbars@overflowmark{slash},
 marker shape/.is choice,
 marker shape/circle/.code=\def\gradbars@markershape{circle},
 marker shape/square/.code=\def\gradbars@markershape{square},
 marker shape/diamond/.code=\def\gradbars@markershape{diamond},
 marker shape/triangle/.code=\def\gradbars@markershape{triangle},
 marker fill/.is choice,
 marker fill/solid/.code=\def\gradbars@markerfill{solid},
 marker fill/hollow/.code=\def\gradbars@markerfill{hollow},
 compare annotation/.is choice,
 compare annotation/none/.code=\def\gradbars@change{none},
 compare annotation/arrow/.code=\def\gradbars@change{arrow},
 compare annotation/bracket/.code=\def\gradbars@change{bracket},
 compare label/percent/.code=\def\gradbars@comparelabel{percent}}
\ExplSyntaxOn
\cs_new_protected:Npn \gradbarsDrawMarker #1#2#3#4
  {\group_begin:
   \tl_set:Nn \l_tmpa_tl {#3}
   \str_if_eq:eeT {#4}{hollow}{\tl_set:Nn \l_tmpa_tl {white}}
   \begin{scope}[shift={(#1,#2)}]
   \tikzset{gradbars~mark/.style={draw=#3,fill=\l_tmpa_tl,line~width=\gradbars@stemwidth}}
   \str_case_e:nn {\gradbars@markershape}
     {{circle}{\path[gradbars~mark] (0,0) circle[radius=\gradbars@markersize];}
      {square}{\path[gradbars~mark] (-\gradbars@markersize,-\gradbars@markersize)
        rectangle (\gradbars@markersize,\gradbars@markersize);}
      {diamond}{\path[gradbars~mark] (0,\gradbars@markersize)--(\gradbars@markersize,0)
        --(0,-\gradbars@markersize)--(-\gradbars@markersize,0)--cycle;}
      {triangle}{\path[gradbars~mark] (0,\gradbars@markersize)
        --(\gradbars@markersize,-\gradbars@markersize)--(-\gradbars@markersize,-\gradbars@markersize)--cycle;}}
   \end{scope}\group_end:}
\cs_set_protected:Npn \gradbarsDrawDot #1
  {\ifgradbars@outline
     \gradbarsDrawMarker{#1}{.5*\gradbars@height}{\gradbars@left}{hollow}
   \else\gradbarsDrawMarker{#1}{.5*\gradbars@height}{\gradbars@left}{\gradbars@markerfill}\fi}
\fp_new:N \l__gradbars_raw_low_fp
\fp_new:N \l__gradbars_raw_high_fp
\bool_new:N \l__gradbars_overflow_known_bool
\cs_new_protected:Npn \gradbarsOverflowSide #1#2
  {\begin{scope}[shift={(#1,.5*\gradbars@height)},xscale=#2]
    \str_if_eq:eeTF {\gradbars@overflowmark}{arrow}
      {\draw[draw=gradbarsInk,line~width=.7pt](-2pt,-2pt)--(1pt,0)--(-2pt,2pt);}
      {\draw[draw=gradbarsInk,line~width=.7pt](-2pt,-2pt)--(0,2pt) (0,-2pt)--(2pt,2pt);}
   \end{scope}}
\cs_new_protected:Npn \gradbarsDrawOverflow
  {\str_if_eq:eeF {\gradbars@overflowmark}{none}
    {\bool_set_true:N \l__gradbars_overflow_known_bool
     \ifgradbars@missing\bool_set_false:N \l__gradbars_overflow_known_bool\fi
     \ifgradbars@compare\ifgradbars@referencemissing\else
       \bool_set_true:N \l__gradbars_overflow_known_bool\fi\fi
     \ifgradbars@spark\bool_set_eq:NN \l__gradbars_overflow_known_bool \l__gradbars_series_any_bool\fi
     \bool_if:NT \l__gradbars_overflow_known_bool {
      \fp_set:Nn \l__gradbars_raw_low_fp {\gradbars@value}
      \fp_set_eq:NN \l__gradbars_raw_high_fp \l__gradbars_raw_low_fp
      \ifgradbars@stack
        \fp_set:Nn \l__gradbars_raw_low_fp {\gradbars@negative}
        \fp_set:Nn \l__gradbars_raw_high_fp {\gradbars@positive}
      \fi
      \ifgradbars@rangemode
        \fp_set:Nn \l__gradbars_raw_low_fp {\gradbars@rangelo}
        \fp_set:Nn \l__gradbars_raw_high_fp {\gradbars@rangehi}
      \fi
      \ifgradbars@compare\ifgradbars@referencemissing\else
        \fp_set:Nn \l__gradbars_raw_low_fp {min(\gradbars@value,\gradbars@reference)}
        \fp_set:Nn \l__gradbars_raw_high_fp {max(\gradbars@value,\gradbars@reference)}
        \ifgradbars@missing
          \fp_set:Nn \l__gradbars_raw_low_fp {\gradbars@reference}
          \fp_set_eq:NN \l__gradbars_raw_high_fp \l__gradbars_raw_low_fp
        \fi
      \fi\fi
      \ifgradbars@error
        \fp_set:Nn \l__gradbars_raw_low_fp {min(\l__gradbars_raw_low_fp,\gradbars@value-\gradbars@errorminus)}
        \fp_set:Nn \l__gradbars_raw_high_fp {max(\l__gradbars_raw_high_fp,\gradbars@value+\gradbars@errorplus)}
      \fi
      \ifgradbars@spark
        \fp_set_eq:NN \l__gradbars_raw_low_fp \l__gradbars_series_min_fp
        \fp_set_eq:NN \l__gradbars_raw_high_fp \l__gradbars_series_max_fp
      \fi
      \fp_compare:nNnT {\l__gradbars_raw_low_fp} < {\gradbars@min}{\gradbarsOverflowSide{0pt}{-1}}
      \fp_compare:nNnT {\l__gradbars_raw_high_fp} > {\gradbars@max}{\gradbarsOverflowSide{\gradbars@width}{1}}
    }}}
\cs_new_protected:Npn \gradbarsChangeLabel
  {\group_begin:
   \ifgradbars@missing\gradbars@missingtext\else
   \ifgradbars@referencemissing\gradbars@missingtext\else
    \str_if_eq:eeTF {\gradbars@comparelabel}{percent}
      {\fp_compare:nNnTF {\gradbars@reference} = {0}
        {\gradbars@missingtext}
        {\def\gradbars@format{value}\def\gradbars@unit{\%}
         \fp_compare:nNnT {\gradbars@value-\gradbars@reference} > {0}{+}
         \gradbarsFormatEndpoint{100*(\gradbars@value-\gradbars@reference)/abs(\gradbars@reference)}}}
      {$\Delta$\,\def\gradbars@format{value}
       \fp_compare:nNnT {\gradbars@value-\gradbars@reference} > {0}{+}
       \gradbarsFormatEndpoint{\gradbars@value-\gradbars@reference}}
   \fi\fi\group_end:}
\cs_new_eq:NN \gradbarsLabelBeforeFour \gradbars@labelcontent
\cs_set_protected:Npn \gradbars@labelcontent
  {\ifgradbars@compare
     \tl_if_empty:oTF {\gradbars@text}
       {\str_if_eq:eeTF {\gradbars@comparelabel}{percent}
          {\gradbarsChangeLabel}{\gradbarsLabelBeforeFour}}
       {\gradbarsLabelBeforeFour}
   \else\gradbarsLabelBeforeFour\fi}
\cs_new_protected:Npn \gradbarsDrawChange
  {\ifgradbars@compare\ifgradbars@missing\else\ifgradbars@referencemissing\else
   \str_if_eq:eeF {\gradbars@change}{none}
    {\pgfmathsetlengthmacro\gradbars@cx{\gradbarsPosition{\gradbars@reference}*\gradbars@width}
     \begin{scope}[yshift={\gradbars@height+5pt}]
       \str_if_eq:eeTF {\gradbars@change}{arrow}
         {\dim_compare:nNnF {\gradbars@xend} = {\gradbars@cx}
            {\draw[->,draw=gradbarsInk,line~width=.5pt](\gradbars@cx,0)--(\gradbars@xend,0);}}
         {\draw[draw=gradbarsInk,line~width=.5pt]
           (\gradbars@cx,-2pt)--(\gradbars@cx,0)--(\gradbars@xend,0)--(\gradbars@xend,-2pt);}
       \node[anchor=south,inner~sep=1pt,font=\scriptsize,text=gradbarsInk]
         at ({.5*(\gradbars@cx+\gradbars@xend)},1pt){\gradbarsChangeLabel};
     \end{scope}}
   \fi\fi\fi}
\ExplSyntaxOff
% gradtable buffers declarations and rows; no alignment tokens in user data.
\ExplSyntaxOn
\seq_new:N \l__gradtable_columns_seq
\seq_new:N \l__gradtable_rows_seq
\prop_new:N \l__gradtable_titles_prop
\prop_new:N \l__gradtable_types_prop
\prop_new:N \l__gradtable_options_prop
\prop_new:N \l__gradtable_widths_prop
\prop_new:N \l__gradtable_align_prop
\prop_new:N \l__gradtable_headalign_prop
\prop_new:N \l__gradtable_valign_prop
\prop_new:N \l__gradtable_colwidth_prop
\tl_new:N \l__gradtable_align_tl
\tl_new:N \l__gradtable_headalign_tl
\tl_new:N \l__gradtable_valign_tl
\tl_new:N \l__gradtable_default_align_tl
\tl_new:N \l__gradtable_default_headalign_tl
\tl_new:N \l__gradtable_default_valign_tl
\tl_new:N \l__gradtable_rules_tl
\tl_new:N \l__gradtable_stretch_tl
\tl_new:N \l__gradtable_header_spec_tl
\tl_new:N \l__gradtable_header_color_tl
\tl_new:N \l__gradtable_rule_color_tl
\dim_new:N \l__gradtable_colwidth_dim
\dim_new:N \l__gradtable_sep_dim
\dim_new:N \l__gradtable_padding_dim
\tl_new:N \l__gradtable_preset_tl
\tl_new:N \l__gradtable_type_tl
\tl_new:N \l__gradtable_options_tl
\tl_new:N \l__gradtable_id_tl
\tl_new:N \l__gradtable_preamble_tl
\tl_new:N \l__gradtable_body_tl
\clist_new:N \l__gradtable_cells_clist
\seq_new:N \l__gradtable_interval_seq
\dim_new:N \l__gradtable_graphic_dim
\dim_new:N \l__gradtable_text_dim
\dim_new:N \l__gradtable_label_dim
\dim_new:N \g__gradtable_measured_dim
\box_new:N \l__gradtable_measure_box
\int_new:N \l__gradtable_row_int
\int_new:N \l__gradtable_col_int
\bool_new:N \l__gradtable_valid_bool
\bool_new:N \l__gradtable_started_bool
\keys_define:nn {gradbars/table}
  {preset .choice:,
   preset / paper .code:n = \tl_set:Nn \l__gradtable_preset_tl {paper},
   preset / report .code:n = \tl_set:Nn \l__gradtable_preset_tl {report},
   preset / mono .code:n = \tl_set:Nn \l__gradtable_preset_tl {mono},
   graphic~width .dim_set:N = \l__gradtable_graphic_dim,
   label~width .dim_set:N = \l__gradtable_label_dim,
   text~width .dim_set:N = \l__gradtable_text_dim,
   align .choices:nn = {auto,l,c,r}{\tl_set_eq:NN\l__gradtable_default_align_tl\l_keys_choice_tl},
   header~align .choices:nn = {auto,l,c,r}{\tl_set_eq:NN\l__gradtable_default_headalign_tl\l_keys_choice_tl},
   valign .choices:nn = {t,c,b}{\tl_set_eq:NN\l__gradtable_default_valign_tl\l_keys_choice_tl},
   rules .choices:nn = {booktabs,rows,grid,none}{\tl_set_eq:NN\l__gradtable_rules_tl\l_keys_choice_tl},
   row~stretch .tl_set:N = \l__gradtable_stretch_tl,
   column~sep .dim_set:N = \l__gradtable_sep_dim,
   row~padding .dim_set:N = \l__gradtable_padding_dim,
   header~color .tl_set:N = \l__gradtable_header_color_tl,
   rule~color .tl_set:N = \l__gradtable_rule_color_tl}
\keys_define:nn {gradbars/table/column}
  {type .choices:nn = {text,number,bar,bullet,interval,compare,dumbbell,stack,spark}
     {\tl_set_eq:NN \l__gradtable_type_tl \l_keys_choice_tl},
   options .tl_set:N = \l__gradtable_options_tl,
   align .choices:nn = {auto,l,c,r}{\tl_set_eq:NN\l__gradtable_align_tl\l_keys_choice_tl},
   header~align .choices:nn = {auto,l,c,r}{\tl_set_eq:NN\l__gradtable_headalign_tl\l_keys_choice_tl},
   valign .choices:nn = {t,c,b}{\tl_set_eq:NN\l__gradtable_valign_tl\l_keys_choice_tl},
   width .dim_set:N = \l__gradtable_colwidth_dim}
\cs_new_protected:Npn \gradtableError #1
  {\bool_set_false:N \l__gradtable_valid_bool
   \PackageError{gradbars}{Table~row~\int_use:N\l__gradtable_row_int,
      ~column~\tl_use:N\l__gradtable_id_tl :~#1}
     {Check~column~declarations~and~comma-separated~row~data.}}
\NewDocumentCommand\gradcolumn{O{} m m}
  {\PackageError{gradbars}{gradcolumn~requires~gradtable}{Place~column~declarations~inside~gradtable.}}
\NewDocumentCommand\gradrow{m}
  {\PackageError{gradbars}{gradrow~requires~gradtable}{Place~rows~inside~gradtable.}}
\cs_new_protected:Npn \gradtableColumn #1#2#3
  {\bool_if:NT \l__gradtable_started_bool {\gradtableError{Declare~columns~before~rows}}
   \seq_if_empty:NF\l__gradtable_headers_seq{\gradtableError{Declare~columns~before~headers}}
   \tl_set:Nn \l__gradtable_id_tl {#2}
   \tl_if_blank:nT {#2}{\gradtableError{Column~identifier~must~not~be~empty}}
   \prop_if_in:NnT \l__gradtable_types_prop {#2}{\gradtableError{Duplicate~column~identifier}}
   \tl_set:Nn \l__gradtable_type_tl {text}
   \tl_clear:N \l__gradtable_options_tl
   \tl_set_eq:NN\l__gradtable_align_tl\l__gradtable_default_align_tl
   \tl_set_eq:NN\l__gradtable_headalign_tl\l__gradtable_default_headalign_tl
   \tl_set_eq:NN\l__gradtable_valign_tl\l__gradtable_default_valign_tl
   \dim_zero:N\l__gradtable_colwidth_dim
   \fp_set:Nn\l__gradtable_weight_fp{1}
   \tl_set:Nn\l__gradtable_mark_tl{none}
   \keys_set:nn {gradbars/table/column}{#1}
   \dim_compare:nNnT{\l__gradtable_colwidth_dim}<{0pt}{\gradtableError{Column~width~must~be~nonnegative}}
   \prop_put:Nnx\l__gradtable_fixed_prop{#2}{\dim_use:N\l__gradtable_colwidth_dim}
   \prop_put:Nnx\l__gradtable_weights_prop{#2}{\fp_use:N\l__gradtable_weight_fp}
   \str_if_eq:VnT\l__gradtable_align_tl{auto}
     {\tl_set:Nn\l__gradtable_align_tl{l}
      \str_if_eq:VnT\l__gradtable_type_tl{number}{\tl_set:Nn\l__gradtable_align_tl{r}}}
   \str_if_eq:VnT\l__gradtable_headalign_tl{auto}{\tl_set_eq:NN\l__gradtable_headalign_tl\l__gradtable_align_tl}
   \str_if_eq:VnT\l__gradtable_type_tl{text}
     {\dim_compare:nNnT{\l__gradtable_colwidth_dim}={0pt}{\dim_set_eq:NN\l__gradtable_colwidth_dim\l__gradtable_text_dim}}
   \prop_put:NnV\l__gradtable_marks_prop{#2}\l__gradtable_mark_tl
   \str_if_eq:VnF\l__gradtable_mark_tl{none}
     {\str_case:VnF\l__gradtable_type_tl{{number}{}{bar}{}{bullet}{}}
       {\gradtableError{Mark~requires~number,~bar~or~bullet~column}}}
   \seq_put_right:Nn \l__gradtable_columns_seq {#2}
   \prop_put:Nnn \l__gradtable_titles_prop {#2}{#3}
   \prop_put:NnV \l__gradtable_types_prop {#2}\l__gradtable_type_tl
   \prop_put:NnV \l__gradtable_options_prop {#2}\l__gradtable_options_tl
   \prop_put:NnV\l__gradtable_align_prop{#2}\l__gradtable_align_tl
   \prop_put:NnV\l__gradtable_headalign_prop{#2}\l__gradtable_headalign_tl
   \prop_put:NnV\l__gradtable_valign_prop{#2}\l__gradtable_valign_tl
   \prop_put:Nnx\l__gradtable_colwidth_prop{#2}{\dim_use:N\l__gradtable_colwidth_dim}
   \prop_put:Nnx \l__gradtable_widths_prop {#2}{\dim_use:N\l__gradtable_label_dim}}
\cs_new_protected:Npn \gradtableRow #1
  {\bool_set_true:N \l__gradtable_started_bool
   \int_incr:N \l__gradtable_row_int
   \clist_set:Nn \l__gradtable_cells_clist {#1}
   \int_compare:nNnTF {\clist_count:N\l__gradtable_cells_clist} = {\seq_count:N\l__gradtable_columns_seq}
     {\seq_put_right:Nn \l__gradtable_rows_seq {#1}}
     {\gradtableError{Wrong~number~of~cells;~use~NA~for~missing~data}}}
\cs_new_protected:Npn \gradtableConfigure #1
  {\prop_get:NnN \l__gradtable_types_prop {#1}\l__gradtable_type_tl
   \prop_get:NnN \l__gradtable_options_prop {#1}\l__gradtable_options_tl
   \pgfkeys{/gradbars,preset=paper,fill=solid,precision=1,overflow~mark=arrow}
   \str_case:Vn \l__gradtable_preset_tl
     {{report}{\pgfkeys{/gradbars,preset=report}}
      {mono}{\pgfkeys{/gradbars,theme=gray,print}}}
   \pgfkeys{/gradbars,width=\l__gradtable_graphic_dim}
   \exp_args:NV \gradbarssetup \l__gradtable_options_tl
   \prop_get:NnNT\l__gradtable_plots_prop{#1}\l_tmpa_tl
     {\pgfkeys{/gradbars/width/.expand~once=\l_tmpa_tl}}
   \pgfkeys{/gradbars,label=outside,label~width=\prop_item:Nn\l__gradtable_widths_prop{#1}}}
\cs_new_protected:Npn \gradtableNumber #1
  {\tl_set:Nn \l_tmpa_tl {#1}\tl_trim_spaces:N \l_tmpa_tl
   \str_if_eq:VnTF \l_tmpa_tl {NA}
     {\gradbars@missingtext}
     {\gradbars@validtrue\gradbarsCheckNumber{#1}{table~number}
      \tl_set:Nx \l_tmpa_tl {\gradbars@precision}
      \regex_match:nVTF {\A [0-6] \Z} \l_tmpa_tl {}
        {\gradbarsFail{Precision~must~be~an~integer~from~0~to~6}}
      \str_if_eq:eeT {\gradbars@format}{percent}
        {\gradbarsCheckNumber{\gradbars@max}{max}
         \ifgradbars@valid\fp_compare:nNnF {\gradbars@max}>{0}
           {\gradbarsFail{Percent~format~requires~positive~max}}\fi}
      \ifgradbars@valid
        \gradbars@missingfalse\def\gradbars@value{#1}
        \gradbarsFormatEndpoint{#1}
      \fi}}
\cs_new_protected:Npn \gradtableContent #1
  {\str_case:Vn \l__gradtable_type_tl
    {{text}{#1}
     {number}{\gradtableNumber{#1}}
     {bar}{\gradbar{#1}}
     {bullet}{\gradbullet{#1}}
     {compare}{\gradtablePair{\gradcompare}{#1}}
     {dumbbell}{\gradtablePair{\graddumbbell}{#1}}
     {stack}{\str_if_eq:nnTF{#1}{NA}{\gradbar{NA}}{\gradstack{#1}}}
     {spark}{\gradspark{#1}}
     {interval}{\str_if_eq:nnTF {#1}{NA}
       {\gradrange{NA}{NA}}
       {\seq_set_split:Nnn \l__gradtable_interval_seq {/}{#1}
        \int_compare:nNnTF {\seq_count:N\l__gradtable_interval_seq} = {3}
          {\tl_set:Nx \l_tmpa_tl {\seq_item:Nn\l__gradtable_interval_seq{2}}
           \str_if_eq:VnT \l_tmpa_tl {NA}{\tl_clear:N\l_tmpa_tl}
           \pgfkeys{/gradbars/range~point/.expand~once=\l_tmpa_tl}
           \exp_args:Nee \gradrange
             {\seq_item:Nn\l__gradtable_interval_seq{1}}
             {\seq_item:Nn\l__gradtable_interval_seq{3}}}
          {\gradbarsFail{Interval~requires~lower/point/upper}}}}}}
\cs_new_protected:Npn \gradtableMeasure #1#2
  {\group_begin:
   \gradbarsSuspendMeasurementTagging
   \tl_set:Nn \l__gradtable_id_tl {#1}
   \gradtableConfigure{#1}
   \gradtableHighlight{#1}{#2}
   \cs_set_eq:NN \gradtableSavedPackageError \PackageError
   \cs_set_protected:Npn \PackageError ##1##2##3
     {\gradtableSavedPackageError{##1}
       {Table~row~\int_use:N\l__gradtable_row_int,~column~#1:~##2}{##3}}
   \dim_gset:Nn \g__gradtable_measured_dim {\prop_item:Nn\l__gradtable_widths_prop{#1}}
   \cs_set_eq:NN \gradtableSavedLabel \gradbarsDrawMainLabel
   \cs_set_protected:Npn \gradbarsDrawMainLabel
     {\gradbarsMeasure\gradbars@labelbox\gradbars@font{\gradbars@labelcontent}
      \dim_gset:Nn \g__gradtable_measured_dim
        {\dim_max:nn{\g__gradtable_measured_dim}{\wd\gradbars@labelbox}}
      \gradtableSavedLabel}
   \hbox_set:Nn \l__gradtable_measure_box {\gradtableContent{#2}}
   \str_if_eq:VnT \l__gradtable_type_tl {number}
     {\dim_gset:Nn \g__gradtable_measured_dim
       {\dim_max:nn{\g__gradtable_measured_dim}{\box_wd:N\l__gradtable_measure_box}}}
   \gradbarsResumeMeasurementTagging
   \group_end:
   \prop_put:Nnx \l__gradtable_widths_prop {#1}{\dim_use:N\g__gradtable_measured_dim}}
\cs_new_protected:Npn \gradtableCell #1#2
  {\group_begin:\tl_set:Nn\l__gradtable_id_tl{#1}\gradtableConfigure{#1}
   \gradtableHighlight{#1}{#2}
   \str_if_eq:VnTF \l__gradtable_type_tl {number}
     {\makebox[\dim_max:nn{\prop_item:Nn\l__gradtable_widths_prop{#1}}{\prop_item:Nn\l__gradtable_colwidth_prop{#1}}]
       [\prop_item:Nn\l__gradtable_align_prop{#1}]{\gradtableContent{#2}}}
     {\str_if_eq:VnTF\l__gradtable_type_tl{text}{\gradtableContent{#2}}
        {\hbox_set:Nn\l__gradtable_measure_box{\gradtableContent{#2}}
         \dim_compare:nNnT{\l__gradtable_total_dim}>{0pt}
           {\dim_compare:nNnT{\box_wd:N\l__gradtable_measure_box}
              >{\prop_item:Nn\l__gradtable_colwidth_prop{#1}+.2pt}
              {\gradtableError{Graphic~exceeds~allocated~column;~increase~width~or~simplify~labels}}}
         \makebox[\dim_max:nn{\box_wd:N\l__gradtable_measure_box}{\prop_item:Nn\l__gradtable_colwidth_prop{#1}}]
           [\prop_item:Nn\l__gradtable_align_prop{#1}]{\box_use:N\l__gradtable_measure_box}}}
   \group_end:}
\cs_new:Npn\gradtableAlignCode #1
  {\str_case:nn{#1}{{l}{\raggedright}{c}{\centering}{r}{\raggedleft}}}
\cs_new:Npn\gradtableVerticalCode #1
  {\str_case:nn{#1}{{t}{p}{c}{m}{b}{b}}}
\cs_new_protected:Npn \gradtableRender
  {\gradtableRank
   \gradtableScan
   \dim_compare:nNnT{\l__gradtable_total_dim}>{0pt}{\gradtableAllocate}
   \tl_set:Nn\l__gradtable_preamble_tl{@{}}
   \tl_clear:N\l__gradtable_body_tl
   \str_case:Vn\l__gradtable_rules_tl
     {{booktabs}{\tl_set:Nn\l__gradtable_body_tl{\toprule}}
      {rows}{\tl_set:Nn\l__gradtable_body_tl{\toprule}}
      {grid}{\tl_set:Nn\l__gradtable_body_tl{\hline}\tl_set:Nn\l__gradtable_preamble_tl{|}}}
   \gradtableBuildHeaders
   \int_zero:N\l__gradtable_col_int
   \seq_map_inline:Nn \l__gradtable_columns_seq
     {\int_incr:N\l__gradtable_col_int
      \int_compare:nNnT{\l__gradtable_col_int}>{1}{\tl_put_right:Nn\l__gradtable_body_tl{&}}
      \bool_if:nTF{\dim_compare_p:nNn{\l__gradtable_total_dim}>{0pt} ||
        \str_if_eq_p:ee{\prop_item:Nn\l__gradtable_types_prop{##1}}{text}}
        {\tl_put_right:Nx\l__gradtable_preamble_tl
          {>{\exp_not:N\gradtableAlignCode{\prop_item:Nn\l__gradtable_align_prop{##1}}\exp_not:N\arraybackslash}
           \str_if_eq:eeTF{\prop_item:Nn\l__gradtable_types_prop{##1}}{text}
             {\exp_args:Ne\gradtableVerticalCode{\prop_item:Nn\l__gradtable_valign_prop{##1}}}{p}
           {\prop_item:Nn\l__gradtable_colwidth_prop{##1}}}}
        {\tl_put_right:Nx\l__gradtable_preamble_tl{\prop_item:Nn\l__gradtable_align_prop{##1}}}
      \str_if_eq:VnT\l__gradtable_rules_tl{grid}{\tl_put_right:Nn\l__gradtable_preamble_tl{|}}
      \tl_clear:N\l__gradtable_header_spec_tl
      \str_if_eq:VnTF\l__gradtable_rules_tl{grid}
        {\int_compare:nNnT{\l__gradtable_col_int}={1}{\tl_put_right:Nn\l__gradtable_header_spec_tl{|}}}
        {\int_compare:nNnT{\l__gradtable_col_int}={1}{\tl_put_right:Nn\l__gradtable_header_spec_tl{@{}}}}
      \tl_if_empty:NF\l__gradtable_header_color_tl
        {\dim_set_eq:NN\l_tmpa_dim\l__gradtable_sep_dim
         \dim_set_eq:NN\l_tmpb_dim\l__gradtable_sep_dim
         \str_if_eq:VnTF\l__gradtable_rules_tl{grid}
           {\dim_set:Nn\l_tmpa_dim{\dim_max:nn{0pt}{\l_tmpa_dim-\arrayrulewidth}}
            \dim_set_eq:NN\l_tmpb_dim\l_tmpa_dim}
           {\int_compare:nNnT{\l__gradtable_col_int}={1}{\dim_zero:N\l_tmpa_dim}
            \int_compare:nNnT{\l__gradtable_col_int}={\seq_count:N\l__gradtable_columns_seq}{\dim_zero:N\l_tmpb_dim}}
         \tl_put_right:Nx\l__gradtable_header_spec_tl
           {>{\exp_not:N\columncolor{\exp_not:V\l__gradtable_header_color_tl}
             [\dim_use:N\l_tmpa_dim][\dim_use:N\l_tmpb_dim]}}}
      \bool_if:nTF{\dim_compare_p:nNn{\l__gradtable_total_dim}>{0pt} ||
        \str_if_eq_p:ee{\prop_item:Nn\l__gradtable_types_prop{##1}}{text}}
        {\tl_put_right:Nx\l__gradtable_header_spec_tl
          {>{\exp_not:N\gradtableAlignCode{\prop_item:Nn\l__gradtable_headalign_prop{##1}}\exp_not:N\arraybackslash}
           p{\prop_item:Nn\l__gradtable_colwidth_prop{##1}}}}
        {\tl_put_right:Nx\l__gradtable_header_spec_tl{\prop_item:Nn\l__gradtable_headalign_prop{##1}}}
      \str_if_eq:VnTF\l__gradtable_rules_tl{grid}
        {\tl_put_right:Nn\l__gradtable_header_spec_tl{|}}
        {\int_compare:nNnT{\l__gradtable_col_int}={\seq_count:N\l__gradtable_columns_seq}
          {\tl_put_right:Nn\l__gradtable_header_spec_tl{@{}}}}
      \tl_put_right:Nx\l__gradtable_body_tl
        {\exp_not:N\multicolumn{1}{\exp_not:V\l__gradtable_header_spec_tl}
          {\exp_not:N\gradtableHeader{##1}}}}
   \str_if_eq:VnF\l__gradtable_rules_tl{grid}{\tl_put_right:Nn\l__gradtable_preamble_tl{@{}}}
   \tl_put_right:Nn\l__gradtable_body_tl{\\}
   \str_case:Vn\l__gradtable_rules_tl
     {{booktabs}{\tl_put_right:Nn\l__gradtable_body_tl{\midrule}}
      {rows}{\tl_put_right:Nn\l__gradtable_body_tl{\midrule}}
      {grid}{\tl_put_right:Nn\l__gradtable_body_tl{\hline}}}
   \tl_set_eq:NN\l__gradtable_head_tl\l__gradtable_body_tl
   \tl_clear:N\l__gradtable_body_tl
   \int_zero:N\l__gradtable_row_int
   \seq_map_inline:Nn\l__gradtable_rows_seq
     {\int_incr:N\l__gradtable_row_int
      \gradtableInsertGroup
      \clist_set:Nn\l__gradtable_cells_clist{##1}
      \int_zero:N\l__gradtable_col_int
      \clist_map_inline:Nn\l__gradtable_cells_clist
        {\int_incr:N\l__gradtable_col_int
         \tl_set:Nx\l__gradtable_id_tl{\seq_item:Nn\l__gradtable_columns_seq{\l__gradtable_col_int}}
         \int_compare:nNnT{\l__gradtable_col_int}>{1}{\tl_put_right:Nn\l__gradtable_body_tl{&}}
         \tl_put_right:Nx\l__gradtable_body_tl
           {\exp_not:N\gradtableCell{\exp_not:V\l__gradtable_id_tl}{\exp_not:n{####1}}}}
      \tl_put_right:Nn\l__gradtable_body_tl{\\}
      \str_if_eq:VnT\l__gradtable_rules_tl{grid}{\tl_put_right:Nn\l__gradtable_body_tl{\hline}}
      \str_if_eq:VnT\l__gradtable_rules_tl{rows}
        {\int_compare:nNnT{\l__gradtable_row_int}<{\seq_count:N\l__gradtable_rows_seq}
          {\tl_put_right:Nn\l__gradtable_body_tl{\midrule}}}}
   \cs_set:Npx\arraystretch{\l__gradtable_stretch_tl}
   \setlength\tabcolsep{\l__gradtable_sep_dim}
   \setlength\extrarowheight{\l__gradtable_padding_dim}
   \exp_args:NV\arrayrulecolor\l__gradtable_rule_color_tl
   \bool_if:NTF\l__gradtable_long_bool{\gradtableLong}
     {\exp_args:NV\tabular\l__gradtable_preamble_tl
        \l__gradtable_head_tl\l__gradtable_body_tl\gradtableBottom
      \endtabular}}
\cs_new_protected:Npn\gradtableHeader #1
  {\textbf{\prop_item:Nn\l__gradtable_titles_prop{#1}}}
\NewDocumentEnvironment{gradtable}{O{} +b}
  {\group_begin:
   \prop_clear:N\l__gradtable_fixed_prop
   \prop_clear:N\l__gradtable_weights_prop
   \prop_clear:N\l__gradtable_plots_prop
   \seq_clear:N\l__gradtable_headers_seq
   \prop_clear:N\l__gradtable_groups_prop
   \prop_clear:N\l__gradtable_marks_prop
   \seq_clear:N\l__gradtable_style_stack_seq
   \seq_clear:N\l__gradtable_schema_stack_seq
   \cs_set_eq:NN\gradtableSavedRuleColor\CT@arc@
   \seq_clear:N\l__gradtable_columns_seq\seq_clear:N\l__gradtable_rows_seq
   \prop_clear:N\l__gradtable_titles_prop\prop_clear:N\l__gradtable_types_prop
   \prop_clear:N\l__gradtable_options_prop\prop_clear:N\l__gradtable_widths_prop
   \prop_clear:N\l__gradtable_align_prop\prop_clear:N\l__gradtable_headalign_prop
   \prop_clear:N\l__gradtable_valign_prop\prop_clear:N\l__gradtable_colwidth_prop
   \int_zero:N\l__gradtable_row_int\tl_clear:N\l__gradtable_id_tl
   \bool_set_true:N\l__gradtable_valid_bool\bool_set_false:N\l__gradtable_started_bool
   \keys_set:nn{gradbars/table}{preset=paper,graphic~width=32mm,text~width=22mm,label~width=18mm,
      align=auto,header~align=auto,valign=t,rules=booktabs,row~stretch=auto,
      column~sep=6pt,row~padding=0pt,header~color=auto,rule~color=black,group~color=gradbarsTrack,group~sep=4pt,width=0pt,min~text~width=12mm,min~graphic~width=12mm,
      long=false,caption={},label={},continued~text={(continued)},#1}
   \str_if_eq:VnT\l__gradtable_stretch_tl{auto}
     {\tl_set:Nn\l__gradtable_stretch_tl{1.2}
      \str_if_eq:VnT\l__gradtable_preset_tl{report}{\tl_set:Nn\l__gradtable_stretch_tl{1.5}}}
   \regex_match:nVTF{\A\d+(\.\d+)?\Z}\l__gradtable_stretch_tl
     {\fp_compare:nNnF{\l__gradtable_stretch_tl}>{0}{\gradtableError{Row~stretch~must~be~positive}}}
     {\gradtableError{Row~stretch~must~be~a~positive~decimal}}
   \dim_compare:nNnT{\l__gradtable_sep_dim}<{0pt}{\gradtableError{Column~sep~must~be~nonnegative}}
   \dim_compare:nNnT{\l__gradtable_padding_dim}<{0pt}{\gradtableError{Row~padding~must~be~nonnegative}}
   \str_if_eq:VnT\l__gradtable_header_color_tl{auto}
     {\tl_clear:N\l__gradtable_header_color_tl
      \str_if_eq:VnT\l__gradtable_preset_tl{report}{\tl_set:Nn\l__gradtable_header_color_tl{gradbarsTrack}}}
   \dim_compare:nNnF{\l__gradtable_graphic_dim}>{0pt}{\gradtableError{Graphic~width~must~be~positive}}
   \dim_compare:nNnF{\l__gradtable_text_dim}>{0pt}{\gradtableError{Text~width~must~be~positive}}
   \dim_compare:nNnT{\l__gradtable_label_dim}<{0pt}{\gradtableError{Label~width~must~be~nonnegative}}
   \gradtableValidateLayout
   \RenewDocumentCommand\gradheader{+m}{\gradtableAddHeader{##1}}
   \RenewDocumentCommand\gradcolumn{O{} m m}{\gradtableColumn{##1}{##2}{##3}}
   \RenewDocumentCommand\gradrow{m}{\gradtableRow{##1}}
   \RenewDocumentCommand\gradgroup{m}{\gradtableGroup{##1}}
   \RenewDocumentCommand\graduseschema{m}{\gradtableUseSchema{##1}}
   #2
   \prop_if_in:NxT\l__gradtable_groups_prop{\int_eval:n{\l__gradtable_row_int+1}}
     {\gradtableError{Group~requires~at~least~one~data~row}}
   \seq_if_empty:NT\l__gradtable_columns_seq{\gradtableError{Table~requires~columns}}
   \bool_if:NT\l__gradtable_valid_bool{\gradtableRender}
   \cs_gset_eq:NN\CT@arc@\gradtableSavedRuleColor
   \group_end:}{}
\ExplSyntaxOff

% Reusable table definitions, group headings and column extrema.
\ExplSyntaxOn
\prop_new:N\g__gradtable_styles_prop
\prop_new:N\g__gradtable_schemas_prop
\prop_new:N\l__gradtable_groups_prop
\prop_new:N\l__gradtable_marks_prop
\prop_new:N\l__gradtable_best_prop
\prop_new:N\l__gradtable_second_prop
\seq_new:N\l__gradtable_style_stack_seq
\seq_new:N\l__gradtable_schema_stack_seq
\seq_new:N\l__gradtable_rank_seq
\tl_new:N\l__gradtable_mark_tl
\tl_new:N\l__gradtable_group_color_tl
\tl_new:N\g__gradtable_rank_direction_tl
\tl_new:N\l__gradtable_rank_direction_tl
\tl_new:N\l__gradtable_rank_value_tl
\dim_new:N\l__gradtable_group_sep_dim
\int_new:N\l__gradtable_rank_col_int
\cs_generate_variant:Nn\prop_if_in:NnT{NxT}
\cs_generate_variant:Nn\prop_get:NnNT{NxNT}
\keys_define:nn{gradbars/table}
  {style .code:n=\gradtableUseStyle{#1},
   group~color .tl_set:N=\l__gradtable_group_color_tl,
   group~sep .code:n={\dim_set:Nn\l__gradtable_group_sep_dim{#1}
     \dim_compare:nNnT{\l__gradtable_group_sep_dim}<{0pt}
       {\gradtableError{Group~sep~must~be~nonnegative}}}}
\keys_define:nn{gradbars/table/column}
  {mark .choices:nn={none,best,both}{\tl_set_eq:NN\l__gradtable_mark_tl\l_keys_choice_tl}}
\NewDocumentCommand\gradtablestyle{m m}
  {\tl_if_blank:nTF{#1}{\PackageError{gradbars}{Empty~table~style~name}{Supply~a~name.}}
     {\prop_gput:Nnn\g__gradtable_styles_prop{#1}{#2}}}
\NewDocumentCommand\gradtableschema{m +m}
  {\tl_if_blank:nTF{#1}{\PackageError{gradbars}{Empty~table~schema~name}{Supply~a~name.}}
     {\prop_gput:Nnn\g__gradtable_schemas_prop{#1}{#2}}}
\cs_new_protected:Npn\gradtableUseStyle #1
  {\seq_if_in:NnTF\l__gradtable_style_stack_seq{#1}
     {\gradtableError{Recursive~table~style~#1}}
     {\prop_get:NnNTF\g__gradtable_styles_prop{#1}\l_tmpa_tl
       {\seq_put_right:Nn\l__gradtable_style_stack_seq{#1}
        \exp_args:NnV\keys_set:nn{gradbars/table}\l_tmpa_tl
        \seq_pop_right:NN\l__gradtable_style_stack_seq\l_tmpa_tl}
       {\gradtableError{Unknown~table~style~#1}}}}
\NewDocumentCommand\graduseschema{m}
  {\PackageError{gradbars}{graduseschema~requires~gradtable}{Use~inside~gradtable.}}
\cs_new_protected:Npn\gradtableUseSchema #1
  {\seq_if_in:NnTF\l__gradtable_schema_stack_seq{#1}
     {\gradtableError{Recursive~table~schema~#1}}
     {\prop_get:NnNTF\g__gradtable_schemas_prop{#1}\l_tmpa_tl
       {\seq_put_right:Nn\l__gradtable_schema_stack_seq{#1}
        \tl_use:N\l_tmpa_tl
        \seq_pop_right:NN\l__gradtable_schema_stack_seq\l_tmpa_tl}
       {\gradtableError{Unknown~table~schema~#1}}}}
\NewDocumentCommand\gradgroup{m}
  {\PackageError{gradbars}{gradgroup~requires~gradtable}{Use~inside~gradtable.}}
\cs_new_protected:Npn\gradtableGroup #1
  {\bool_set_true:N\l__gradtable_started_bool
   \seq_if_empty:NT\l__gradtable_columns_seq{\gradtableError{Declare~columns~before~groups}}
   \tl_if_blank:nT{#1}{\gradtableError{Group~title~must~not~be~empty}}
   \prop_if_in:NxT\l__gradtable_groups_prop{\int_eval:n{\l__gradtable_row_int+1}}
     {\gradtableError{Group~requires~at~least~one~data~row}}
   \prop_put:Nxn\l__gradtable_groups_prop{\int_eval:n{\l__gradtable_row_int+1}}{#1}}
\cs_new_protected:Npn\gradtableInsertGroup
  {\prop_get:NxNT\l__gradtable_groups_prop{\int_use:N\l__gradtable_row_int}\l_tmpa_tl
    {\str_if_eq:VnF\l__gradtable_rules_tl{grid}
       {\tl_put_right:Nx\l__gradtable_body_tl
          {\exp_not:N\noalign{\exp_not:N\vskip\dim_use:N\l__gradtable_group_sep_dim}}}
     \tl_set:Nn\l_tmpb_tl{@{}}
     \dim_zero:N\l_tmpa_dim
     \str_if_eq:VnT\l__gradtable_rules_tl{grid}
       {\tl_set:Nn\l_tmpb_tl{|}
        \dim_set:Nn\l_tmpa_dim{\dim_max:nn{0pt}{\l__gradtable_sep_dim-\arrayrulewidth}}}
     \tl_if_empty:NF\l__gradtable_group_color_tl
       {\tl_put_right:Nx\l_tmpb_tl
         {>{\exp_not:N\columncolor{\exp_not:V\l__gradtable_group_color_tl}
           [\dim_use:N\l_tmpa_dim][\dim_use:N\l_tmpa_dim]}}}
     \dim_compare:nNnTF{\l__gradtable_total_dim}>{0pt}
       {\dim_set_eq:NN\l__gradtable_span_dim\l__gradtable_total_dim
        \str_if_eq:VnT\l__gradtable_rules_tl{grid}
          {\dim_sub:Nn\l__gradtable_span_dim{2\l__gradtable_sep_dim+2\arrayrulewidth}}
        \tl_put_right:Nx\l_tmpb_tl
          {>{\exp_not:N\raggedright\exp_not:N\arraybackslash}p{\dim_use:N\l__gradtable_span_dim}}}
       {\tl_put_right:Nn\l_tmpb_tl{l}}
     \str_if_eq:VnTF\l__gradtable_rules_tl{grid}
       {\tl_put_right:Nn\l_tmpb_tl{|}}{\tl_put_right:Nn\l_tmpb_tl{@{}}}
     \tl_put_right:Nx\l__gradtable_body_tl
       {\exp_not:N\multicolumn{\seq_count:N\l__gradtable_columns_seq}{\exp_not:V\l_tmpb_tl}
         {\exp_not:N\gradtableGroupTitle{\exp_not:V\l_tmpa_tl}}\exp_not:N\\*}
     \str_if_eq:VnT\l__gradtable_rules_tl{grid}
       {\tl_put_right:Nn\l__gradtable_body_tl{\gradtableGroupRule}}}}
\cs_new:Npn\gradtableGroupRule
  {\noalign{\nobreak{\CT@arc@\hrule height\arrayrulewidth}\nobreak}}
\cs_new_protected:Npn\gradtableGroupTitle #1
  {\rule{0pt}{\dim_eval:n{\ht\strutbox+\l__gradtable_group_sep_dim}}\textbf{#1}}
\cs_new_protected:Npn\gradtablePair #1#2
  {\str_if_eq:nnTF{#2}{NA}{#1{NA}{NA}}
    {\seq_set_split:Nnn\l__gradtable_interval_seq{/}{#2}
     \int_compare:nNnTF{\seq_count:N\l__gradtable_interval_seq}={2}
       {\exp_args:Nee#1{\seq_item:Nn\l__gradtable_interval_seq{1}}
         {\seq_item:Nn\l__gradtable_interval_seq{2}}}
       {\gradbarsFail{Comparison~requires~reference/current}}}}
\cs_new_protected:Npn\gradtableRank
  {\prop_clear:N\l__gradtable_best_prop\prop_clear:N\l__gradtable_second_prop
   \int_zero:N\l__gradtable_rank_col_int
   \seq_map_inline:Nn\l__gradtable_columns_seq
     {\int_incr:N\l__gradtable_rank_col_int
      \str_if_eq:eeF{\prop_item:Nn\l__gradtable_marks_prop{##1}}{none}
        {\group_begin:\gradtableConfigure{##1}
         \tl_gset:Nx\g__gradtable_rank_direction_tl{\gradbars@better}\group_end:
         \tl_set_eq:NN\l__gradtable_rank_direction_tl\g__gradtable_rank_direction_tl
         \str_case:VnF\l__gradtable_rank_direction_tl{{higher}{}{lower}{}}
           {\gradtableError{Mark~requires~better=higher~or~lower}}
         \seq_clear:N\l__gradtable_rank_seq
         \seq_map_inline:Nn\l__gradtable_rows_seq
           {\clist_set:Nn\l__gradtable_cells_clist{####1}
            \tl_set:Nx\l__gradtable_rank_value_tl
              {\clist_item:Nn\l__gradtable_cells_clist{\l__gradtable_rank_col_int}}
            \tl_trim_spaces:N\l__gradtable_rank_value_tl
            \regex_match:nVT{\A[+\-]?(\d+(\.\d*)?|\.\d+)\Z}\l__gradtable_rank_value_tl
              {\seq_put_right:Nx\l__gradtable_rank_seq{\fp_eval:n{\l__gradtable_rank_value_tl}}}}
         \seq_remove_duplicates:N\l__gradtable_rank_seq
         \seq_sort:Nn\l__gradtable_rank_seq
           {\str_if_eq:VnTF\l__gradtable_rank_direction_tl{higher}
              {\fp_compare:nNnTF{####1}<{####2}{\sort_return_swapped:}{\sort_return_same:}}
              {\fp_compare:nNnTF{####1}>{####2}{\sort_return_swapped:}{\sort_return_same:}}}
         \seq_if_empty:NF\l__gradtable_rank_seq
           {\prop_put:Nnx\l__gradtable_best_prop{##1}{\seq_item:Nn\l__gradtable_rank_seq{1}}}
         \int_compare:nNnT{\seq_count:N\l__gradtable_rank_seq}>{1}
           {\prop_put:Nnx\l__gradtable_second_prop{##1}{\seq_item:Nn\l__gradtable_rank_seq{2}}}}}}
\cs_new_protected:Npn\gradtableBest #1{{\bfseries\boldmath #1}}
\cs_new_protected:Npn\gradtableHighlight #1#2
  {\cs_set_eq:NN\gradtableEmphasis\use:n
   \tl_set:Nn\l_tmpa_tl{#2}\tl_trim_spaces:N\l_tmpa_tl
   \regex_match:nVT{\A[+\-]?(\d+(\.\d*)?|\.\d+)\Z}\l_tmpa_tl
     {\prop_get:NnNT\l__gradtable_best_prop{#1}\l_tmpb_tl
        {\fp_compare:nNnT{#2}={\l_tmpb_tl}{\cs_set_eq:NN\gradtableEmphasis\gradtableBest}}
      \str_if_eq:eeT{\prop_item:Nn\l__gradtable_marks_prop{#1}}{both}
        {\prop_get:NnNT\l__gradtable_second_prop{#1}\l_tmpb_tl
          {\fp_compare:nNnT{#2}={\l_tmpb_tl}{\cs_set_eq:NN\gradtableEmphasis\underline}}}}
   \str_if_eq:VnTF\l__gradtable_type_tl{number}
     {\cs_set_eq:NN\gradtableOriginalNumber\gradtableNumber
      \cs_set_protected:Npn\gradtableNumber##1{\gradtableEmphasis{\gradtableOriginalNumber{##1}}}}
     {\cs_set_eq:NN\gradtableOriginalLabel\gradbars@labelcontent
      \cs_set_protected:Npn\gradbars@labelcontent{\gradtableEmphasis{\gradtableOriginalLabel}}}}
\ExplSyntaxOff
% v0.0.4: column-spanning headers, constrained widths and long tables.
\ExplSyntaxOn
\seq_new:N\l__gradtable_headers_seq
\tl_new:N\l__gradtable_head_tl
\tl_new:N\l__gradtable_caption_tl
\tl_new:N\l__gradtable_label_tl
\tl_new:N\l__gradtable_continued_tl
\tl_new:N\l__gradtable_span_id_tl
\bool_new:N\l__gradtable_long_bool
\dim_new:N\l__gradtable_total_dim
\dim_new:N\l__gradtable_min_text_dim
\dim_new:N\l__gradtable_min_graphic_dim
\dim_new:N\l__gradtable_remaining_dim
\dim_new:N\l__gradtable_base_dim
\dim_new:N\l__gradtable_span_dim
\dim_new:N\g__gradtable_extent_dim
\fp_new:N\l__gradtable_weight_fp
\fp_new:N\l__gradtable_weight_sum_fp
\prop_new:N\l__gradtable_fixed_prop
\prop_new:N\l__gradtable_weights_prop
\prop_new:N\l__gradtable_flex_prop
\prop_new:N\l__gradtable_plots_prop
\prop_new:N\l__gradtable_overheads_prop
\int_new:N\l__gradtable_span_start_int
\int_new:N\l__gradtable_span_count_int
\keys_define:nn{gradbars/table}
  {width .dim_set:N=\l__gradtable_total_dim,
   min~text~width .dim_set:N=\l__gradtable_min_text_dim,
   min~graphic~width .dim_set:N=\l__gradtable_min_graphic_dim,
   long .bool_set:N=\l__gradtable_long_bool,
   caption .tl_set:N=\l__gradtable_caption_tl,
   label .tl_set:N=\l__gradtable_label_tl,
   continued~text .tl_set:N=\l__gradtable_continued_tl}
\keys_define:nn{gradbars/table/column}
  {weight .code:n={\regex_match:nnTF{\A\d+(\.\d+)?\Z}{#1}
     {\fp_set:Nn\l__gradtable_weight_fp{#1}
      \fp_compare:nNnF{#1}>{0}{\gradtableError{Column~weight~must~be~positive}}}
     {\gradtableError{Column~weight~must~be~a~positive~decimal}}}}
\cs_new_protected:Npn\gradtableValidateLayout
  {\dim_compare:nNnT{\l__gradtable_total_dim}<{0pt}{\gradtableError{Table~width~must~be~nonnegative}}
   \dim_compare:nNnF{\l__gradtable_min_text_dim}>{0pt}{\gradtableError{Min~text~width~must~be~positive}}
   \dim_compare:nNnF{\l__gradtable_min_graphic_dim}>{0pt}{\gradtableError{Min~graphic~width~must~be~positive}}
   \bool_if:NTF\l__gradtable_long_bool
     {\ifinner\gradtableError{Long~table~requires~outer~paragraph~mode}\fi
      \if@twocolumn\gradtableError{Long~table~requires~one-column~mode}\fi
      \tl_if_empty:NF\l__gradtable_label_tl
        {\tl_if_empty:NT\l__gradtable_caption_tl{\gradtableError{Table~label~requires~caption}}}}
     {\tl_if_empty:NF\l__gradtable_caption_tl{\gradtableError{Caption~option~requires~long=true}}
      \tl_if_empty:NF\l__gradtable_label_tl{\gradtableError{Label~option~requires~long=true}}}}
\NewDocumentCommand\gradheader{+m}
  {\PackageError{gradbars}{gradheader~requires~gradtable}{Declare~headers~inside~gradtable.}}
\NewDocumentCommand\gradspan{m m}
  {\PackageError{gradbars}{gradspan~requires~gradheader}{Place~spans~inside~gradheader.}}
\cs_new_protected:Npn\gradtableAddHeader #1
  {\bool_if:NT\l__gradtable_started_bool{\gradtableError{Declare~headers~before~rows}}
   \int_zero:N\l__gradtable_span_count_int
   \cs_set_eq:NN\gradtableSavedSpan\gradspan
   \RenewDocumentCommand\gradspan{m m}
     {\regex_match:nnTF{\A[1-9]\d*\Z}{##1}
       {\int_add:Nn\l__gradtable_span_count_int{##1}}
       {\gradtableError{Header~span~must~be~a~positive~integer}}}
   #1
   \cs_set_eq:NN\gradspan\gradtableSavedSpan
   \int_compare:nNnF{\l__gradtable_span_count_int}={\seq_count:N\l__gradtable_columns_seq}
     {\gradtableError{Header~spans~must~cover~all~columns~exactly}}
   \seq_put_right:Nn\l__gradtable_headers_seq{#1}}
\cs_new_protected:Npn\gradtableBuildHeaders
  {\seq_map_inline:Nn\l__gradtable_headers_seq
    {\int_set:Nn\l__gradtable_span_start_int{1}
     \cs_set_eq:NN\gradtableSavedSpan\gradspan
     \RenewDocumentCommand\gradspan{m m}{\gradtableBuildSpan{####1}{####2}}
     ##1
     \cs_set_eq:NN\gradspan\gradtableSavedSpan
     \tl_put_right:Nn\l__gradtable_body_tl{\\}
     \str_case:Vn\l__gradtable_rules_tl
       {{grid}{\tl_put_right:Nn\l__gradtable_body_tl{\hline}}
        {booktabs}{\tl_put_right:Nn\l__gradtable_body_tl{\midrule}}
        {rows}{\tl_put_right:Nn\l__gradtable_body_tl{\midrule}}}}}
\cs_new_protected:Npn\gradtableBuildSpan #1#2
  {\int_compare:nNnT{\l__gradtable_span_start_int}>{1}{\tl_put_right:Nn\l__gradtable_body_tl{&}}
   \tl_clear:N\l__gradtable_header_spec_tl
   \str_if_eq:VnTF\l__gradtable_rules_tl{grid}
     {\int_compare:nNnT{\l__gradtable_span_start_int}={1}{\tl_put_right:Nn\l__gradtable_header_spec_tl{|}}}
     {\int_compare:nNnT{\l__gradtable_span_start_int}={1}{\tl_put_right:Nn\l__gradtable_header_spec_tl{@{}}}}
   \tl_if_empty:NF\l__gradtable_header_color_tl
     {\dim_set_eq:NN\l_tmpa_dim\l__gradtable_sep_dim
      \dim_set_eq:NN\l_tmpb_dim\l__gradtable_sep_dim
      \str_if_eq:VnTF\l__gradtable_rules_tl{grid}
        {\dim_set:Nn\l_tmpa_dim{\dim_max:nn{0pt}{\l_tmpa_dim-\arrayrulewidth}}
         \dim_set_eq:NN\l_tmpb_dim\l_tmpa_dim}
        {\int_compare:nNnT{\l__gradtable_span_start_int}={1}{\dim_zero:N\l_tmpa_dim}
         \int_compare:nNnT{\l__gradtable_span_start_int+#1-1}={\seq_count:N\l__gradtable_columns_seq}
           {\dim_zero:N\l_tmpb_dim}}
      \tl_put_right:Nx\l__gradtable_header_spec_tl
        {>{\exp_not:N\columncolor{\exp_not:V\l__gradtable_header_color_tl}
          [\dim_use:N\l_tmpa_dim][\dim_use:N\l_tmpb_dim]}}}
   \dim_compare:nNnTF{\l__gradtable_total_dim}>{0pt}
     {\dim_set:Nn\l__gradtable_span_dim{2\l__gradtable_sep_dim*(#1-1)}
      \str_if_eq:VnT\l__gradtable_rules_tl{grid}
        {\dim_add:Nn\l__gradtable_span_dim{\arrayrulewidth*(#1-1)}}
      \int_step_inline:nnnn{\l__gradtable_span_start_int}{1}{\l__gradtable_span_start_int+#1-1}
        {\tl_set:Nx\l__gradtable_span_id_tl{\seq_item:Nn\l__gradtable_columns_seq{##1}}
         \dim_add:Nn\l__gradtable_span_dim{\prop_item:NV\l__gradtable_colwidth_prop\l__gradtable_span_id_tl}}
      \tl_put_right:Nx\l__gradtable_header_spec_tl
        {>{\exp_not:N\centering\exp_not:N\arraybackslash}p{\dim_use:N\l__gradtable_span_dim}}}
     {\tl_put_right:Nn\l__gradtable_header_spec_tl{c}}
   \str_if_eq:VnTF\l__gradtable_rules_tl{grid}
     {\tl_put_right:Nn\l__gradtable_header_spec_tl{|}}
     {\int_compare:nNnT{\l__gradtable_span_start_int+#1-1}={\seq_count:N\l__gradtable_columns_seq}
        {\tl_put_right:Nn\l__gradtable_header_spec_tl{@{}}}}
   \tl_put_right:Nx\l__gradtable_body_tl
     {\exp_not:N\multicolumn{#1}{\exp_not:V\l__gradtable_header_spec_tl}{\exp_not:N\textbf{\exp_not:n{#2}}}}
   \int_add:Nn\l__gradtable_span_start_int{#1}}
\cs_new_protected:Npn\gradtableScan
  {\int_zero:N\l__gradtable_row_int
   \seq_map_inline:Nn\l__gradtable_rows_seq
     {\int_incr:N\l__gradtable_row_int
      \clist_set:Nn\l__gradtable_cells_clist{##1}
      \int_zero:N\l__gradtable_col_int
      \clist_map_inline:Nn\l__gradtable_cells_clist
        {\int_incr:N\l__gradtable_col_int
         \tl_set:Nx\l__gradtable_id_tl{\seq_item:Nn\l__gradtable_columns_seq{\l__gradtable_col_int}}
         \exp_args:NV\gradtableMeasure\l__gradtable_id_tl{####1}}}}
\cs_new_protected:Npn\gradtableExtent #1#2
  {\group_begin:\gradtableConfigure{#1}\gradtableHighlight{#1}{#2}
   \gradbarsSuspendMeasurementTagging
   \dim_gset:Nn\g__gradtable_extent_dim{\prop_item:Nn\l__gradtable_overheads_prop{#1}}
   \str_case:VnF\l__gradtable_type_tl{{text}{}{number}{}}
     {\hbox_set:Nn\l__gradtable_measure_box{\gradtableContent{#2}}
      \dim_gset:Nn\g__gradtable_extent_dim
        {\dim_max:nn{\prop_item:Nn\l__gradtable_overheads_prop{#1}}
          {\box_wd:N\l__gradtable_measure_box-\gradbars@width}}}
   \gradbarsResumeMeasurementTagging
   \group_end:
   \prop_put:Nnx\l__gradtable_overheads_prop{#1}{\dim_use:N\g__gradtable_extent_dim}}
\cs_new_protected:Npn\gradtableAllocate
  {\prop_clear:N\l__gradtable_overheads_prop\prop_clear:N\l__gradtable_flex_prop
   \seq_map_inline:Nn\l__gradtable_columns_seq
     {\group_begin:\gradtableConfigure{##1}
      \dim_gset:Nn\g__gradtable_extent_dim
        {\prop_item:Nn\l__gradtable_widths_prop{##1}+\gradbars@gap+2pt}\group_end:
      \prop_put:Nnx\l__gradtable_overheads_prop{##1}{\dim_use:N\g__gradtable_extent_dim}}
   \seq_map_inline:Nn\l__gradtable_rows_seq
     {\clist_set:Nn\l__gradtable_cells_clist{##1}\int_zero:N\l__gradtable_col_int
      \clist_map_inline:Nn\l__gradtable_cells_clist
        {\int_incr:N\l__gradtable_col_int
         \tl_set:Nx\l__gradtable_id_tl{\seq_item:Nn\l__gradtable_columns_seq{\l__gradtable_col_int}}
         \exp_args:NV\gradtableExtent\l__gradtable_id_tl{####1}}}
   \dim_set:Nn\l__gradtable_remaining_dim
     {\l__gradtable_total_dim-2\l__gradtable_sep_dim*(\seq_count:N\l__gradtable_columns_seq-1)}
   \str_if_eq:VnT\l__gradtable_rules_tl{grid}
     {\dim_sub:Nn\l__gradtable_remaining_dim
        {2\l__gradtable_sep_dim+\arrayrulewidth*(\seq_count:N\l__gradtable_columns_seq+1)}}
   \fp_zero:N\l__gradtable_weight_sum_fp
   \seq_map_inline:Nn\l__gradtable_columns_seq
     {\tl_set:Nn\l__gradtable_id_tl{##1}
      \tl_set:Nx\l__gradtable_type_tl{\prop_item:Nn\l__gradtable_types_prop{##1}}
      \dim_set:Nn\l__gradtable_base_dim{\prop_item:Nn\l__gradtable_fixed_prop{##1}}
      \fp_zero:N\l__gradtable_weight_fp
      \str_if_eq:VnTF\l__gradtable_type_tl{number}
        {\dim_set:Nn\l__gradtable_base_dim
           {\dim_max:nn{\l__gradtable_base_dim}{\prop_item:Nn\l__gradtable_widths_prop{##1}}}}
        {\dim_compare:nNnTF{\l__gradtable_base_dim}>{0pt}
           {\str_if_eq:VnF\l__gradtable_type_tl{text}
              {\dim_compare:nNnT{\l__gradtable_base_dim}
                <{\prop_item:Nn\l__gradtable_overheads_prop{##1}+\l__gradtable_min_graphic_dim}
                {\gradtableError{Fixed~column~too~narrow~for~labels~and~graphic}}}}
           {\fp_set:Nn\l__gradtable_weight_fp{\prop_item:Nn\l__gradtable_weights_prop{##1}}
            \str_if_eq:VnTF\l__gradtable_type_tl{text}
              {\dim_set_eq:NN\l__gradtable_base_dim\l__gradtable_min_text_dim}
              {\dim_set:Nn\l__gradtable_base_dim
                {\prop_item:Nn\l__gradtable_overheads_prop{##1}+\l__gradtable_min_graphic_dim}}}}
      \prop_put:Nnx\l__gradtable_colwidth_prop{##1}{\dim_use:N\l__gradtable_base_dim}
      \prop_put:Nnx\l__gradtable_flex_prop{##1}{\fp_use:N\l__gradtable_weight_fp}
      \fp_add:Nn\l__gradtable_weight_sum_fp{\l__gradtable_weight_fp}
      \dim_sub:Nn\l__gradtable_remaining_dim{\l__gradtable_base_dim}}
   \dim_compare:nNnT{\l__gradtable_remaining_dim}<{0pt}
     {\gradtableError{Table~width~too~small;~increase~width~or~reduce~minimum~widths~and~spacing}}
   \fp_compare:nNnT{\l__gradtable_weight_sum_fp}={0}
     {\dim_compare:nNnT{\l__gradtable_remaining_dim}>{.1pt}
        {\gradtableError{Table~width~requires~a~flexible~text~or~graphic~column}}}
   \seq_map_inline:Nn\l__gradtable_columns_seq
     {\fp_set:Nn\l__gradtable_weight_fp{\prop_item:Nn\l__gradtable_flex_prop{##1}}
      \fp_compare:nNnT{\l__gradtable_weight_fp}>{0}
        {\dim_set:Nn\l_tmpa_dim{\fp_to_dim:n
           {\dim_to_fp:n{\l__gradtable_remaining_dim}*\l__gradtable_weight_fp/\l__gradtable_weight_sum_fp}}
         \prop_put:Nnx\l__gradtable_colwidth_prop{##1}
           {\dim_eval:n{\prop_item:Nn\l__gradtable_colwidth_prop{##1}+\l_tmpa_dim}}
         \dim_sub:Nn\l__gradtable_remaining_dim{\l_tmpa_dim}
         \fp_sub:Nn\l__gradtable_weight_sum_fp{\l__gradtable_weight_fp}}
      \str_case_e:nnF{\prop_item:Nn\l__gradtable_types_prop{##1}}{{text}{}{number}{}}
        {\prop_put:Nnx\l__gradtable_plots_prop{##1}
          {\dim_eval:n{\prop_item:Nn\l__gradtable_colwidth_prop{##1}-\prop_item:Nn\l__gradtable_overheads_prop{##1}}}}}}
\cs_new:Npn\gradtableBottom
  {\str_case:Vn\l__gradtable_rules_tl{{booktabs}{\bottomrule}{rows}{\bottomrule}}}
\cs_new_protected:Npn\gradtableLong
  {\par\setlength\LTleft{0pt}\setlength\LTright{\fill}
   \dim_compare:nNnT{\l__gradtable_total_dim}>{0pt}{\setlength\LTcapwidth{\l__gradtable_total_dim}}
   \exp_args:NV\longtable\l__gradtable_preamble_tl
     \tl_if_empty:NF\l__gradtable_caption_tl
       {\caption{\l__gradtable_caption_tl}
        \tl_if_empty:NF\l__gradtable_label_tl{\exp_args:NV\label\l__gradtable_label_tl}\\}
     \l__gradtable_head_tl\endfirsthead
     \tl_if_empty:NTF\l__gradtable_caption_tl
       {\tl_if_empty:NF\l__gradtable_continued_tl
         {\multicolumn{\seq_count:N\l__gradtable_columns_seq}{c}{\l__gradtable_continued_tl}\\}}
       {\caption[]{\l__gradtable_caption_tl\space\l__gradtable_continued_tl}\\}
     \l__gradtable_head_tl\endhead
     \gradtableBottom
     \tl_if_empty:NF\l__gradtable_continued_tl
       {\multicolumn{\seq_count:N\l__gradtable_columns_seq}{r}{\footnotesize\l__gradtable_continued_tl}\\}
     \endfoot
     \gradtableBottom\endlastfoot
     \l__gradtable_body_tl
   \endlongtable}
\ExplSyntaxOff

% Accessible descriptions use the supported TikZ tagging interface.  No tagging
% is activated by this package; ordinary documents keep their existing output.
\def\gradbarsCaptureTikzAlt{%
  % Capture explicit /tikz/alt options locally, without changing the global key.
  \pgfkeysifdefined{/tikz/alt/.@cmd}{%
    \pgfkeys{/tikz/alt/.append code={\ifgradbars@capturealt\def\gradbars@alt{##1}\fi}}%
  }{\pgfkeys{/tikz/alt/.code={\def\gradbars@alt{##1}}}}%
}
\ExplSyntaxOn
\tl_new:N \l__gradbars_alt_tl
\seq_new:N \l__gradbars_alt_fields_seq
\int_new:N \l__gradbars_alt_index_int
\cs_new_protected:Npn \gradbarsAltValue #1
  {
    \tl_set:Nx \l_tmpa_tl {#1}
    \tl_trim_spaces:N \l_tmpa_tl
    \tl_if_empty:NTF \l_tmpa_tl
      {\tl_put_right:Nn \l__gradbars_alt_tl {missing}}
      {\str_if_eq:VnTF \l_tmpa_tl {NA}
        {\tl_put_right:Nn \l__gradbars_alt_tl {missing}}
        {\tl_put_right:Nx \l__gradbars_alt_tl {\fp_eval:n {\l_tmpa_tl}}}}
  }
\cs_new_protected:Npn \gradbarsBuildAlt
  {
    \tl_clear:N \l__gradbars_alt_tl
    \ifgradbars@spark
      \tl_put_right:Nn \l__gradbars_alt_tl {Equally~spaced~series:~}
      \int_zero:N \l__gradbars_alt_index_int
      \seq_map_inline:Nn \l__gradbars_series_seq
        {\int_incr:N \l__gradbars_alt_index_int
         \int_compare:nNnT {\l__gradbars_alt_index_int}>{1}
           {\tl_put_right:Nn \l__gradbars_alt_tl {;~}}
         \tl_put_right:Nx \l__gradbars_alt_tl
           {observation~\int_use:N\l__gradbars_alt_index_int :~}
         \gradbarsAltValue{##1}}
      \tl_put_right:Nn \l__gradbars_alt_tl {.~Final~observation:~}
      \gradbarsAltValue{\gradbars@sparklast}
      \tl_put_right:Nx \l__gradbars_alt_tl
        {.~Vertical~range:~\fp_use:N\l__gradbars_series_low_fp\c_space_tl to~\fp_use:N\l__gradbars_series_high_fp.}
    \else\ifgradbars@stack
      \tl_put_right:Nn \l__gradbars_alt_tl {Stacked~contributions:~}
      \int_zero:N \l__gradbars_alt_index_int
      \clist_map_inline:Nn \l__gradbars_stack_clist
        {\int_incr:N \l__gradbars_alt_index_int
         \int_compare:nNnT {\l__gradbars_alt_index_int}>{1}
           {\tl_put_right:Nn \l__gradbars_alt_tl {;~}}
         \clist_if_empty:NTF \l__gradbars_names_clist
           {\tl_put_right:Nx \l__gradbars_alt_tl
             {segment~\int_use:N\l__gradbars_alt_index_int}}
           {\tl_put_right:Nx \l__gradbars_alt_tl
             {\clist_item:Nn\l__gradbars_names_clist{\l__gradbars_alt_index_int}}}
         \tl_put_right:Nn \l__gradbars_alt_tl {:~}\gradbarsAltValue{##1}}
      \tl_put_right:Nx \l__gradbars_alt_tl
        {.~Net:~\gradbars@total.~Positive~subtotal:~\gradbars@positive.\c_space_tl
         Negative~subtotal:~\gradbars@negative.}
    \else\ifgradbars@compare
      \tl_put_right:Nn \l__gradbars_alt_tl {Reference:~}
      \ifgradbars@referencemissing
        \tl_put_right:Nn \l__gradbars_alt_tl {missing}
      \else\gradbarsAltValue{\gradbars@reference}\fi
      \tl_put_right:Nn \l__gradbars_alt_tl {.~Current:~}
      \ifgradbars@missing\tl_put_right:Nn \l__gradbars_alt_tl {missing}
      \else\gradbarsAltValue{\gradbars@value}\fi
      \tl_put_right:Nn \l__gradbars_alt_tl {.~Change:~}
      \ifgradbars@missing\tl_put_right:Nn \l__gradbars_alt_tl {missing}
      \else\ifgradbars@referencemissing\tl_put_right:Nn \l__gradbars_alt_tl {missing}
      \else\gradbarsAltValue{\gradbars@value-\gradbars@reference}\fi\fi
      \str_if_eq:eeT {\gradbars@comparelabel}{percent}
        {\tl_put_right:Nn \l__gradbars_alt_tl {.~Relative~change:~}
         \ifgradbars@missing\tl_put_right:Nn \l__gradbars_alt_tl {missing}
         \else\ifgradbars@referencemissing\tl_put_right:Nn \l__gradbars_alt_tl {missing}
         \else\fp_compare:nNnTF {\gradbars@reference}={0}
           {\tl_put_right:Nn \l__gradbars_alt_tl {undefined~for~a~zero~reference}}
           {\gradbarsAltValue{100*(\gradbars@value-\gradbars@reference)/abs(\gradbars@reference)}
            \tl_put_right:Nn \l__gradbars_alt_tl {~percent}}\fi\fi}
      \tl_put_right:Nn \l__gradbars_alt_tl {.}
    \else\ifgradbars@rangemode
      \tl_put_right:Nn \l__gradbars_alt_tl {Interval:~}
      \ifgradbars@rangemissing\tl_put_right:Nn \l__gradbars_alt_tl {missing.}
      \else
        \gradbarsAltValue{\gradbars@rangelo}
        \tl_put_right:Nn \l__gradbars_alt_tl {~to~}\gradbarsAltValue{\gradbars@rangehi}
        \tl_put_right:Nn \l__gradbars_alt_tl {.}
        \tl_if_empty:oF {\gradbars@rangepoint}
          {\tl_put_right:Nn \l__gradbars_alt_tl {~Point~estimate:~}
           \gradbarsAltValue{\gradbars@rangepoint}\tl_put_right:Nn \l__gradbars_alt_tl {.}}
      \fi
    \else
      \tl_put_right:Nn \l__gradbars_alt_tl {Value:~}
      \ifgradbars@missing\tl_put_right:Nn \l__gradbars_alt_tl {missing.}
      \else\gradbarsAltValue{\gradbars@value}\tl_put_right:Nn \l__gradbars_alt_tl {.}\fi
    \fi\fi\fi\fi
    \tl_if_empty:oF {\gradbars@unit}
      {\tl_put_right:Nx \l__gradbars_alt_tl {~Unit:~\exp_not:o{\gradbars@unit}.}}
    \ifgradbars@spark\else
      \tl_put_right:Nx \l__gradbars_alt_tl
        {~Scale:~\fp_eval:n{\gradbars@min}~to~\fp_eval:n{\gradbars@max}.}
      \ifgradbars@error
        \tl_put_right:Nx \l__gradbars_alt_tl
          {~Error~distances:~minus~\gradbars@errorminus,~plus~\gradbars@errorplus.}
      \fi
      \tl_if_empty:oF {\gradbars@target}
        {\tl_put_right:Nx \l__gradbars_alt_tl {~Target:~\gradbars@target.}}
      \clist_map_inline:Nn \l__gradbars_refs_clist
        {\seq_set_split:Nnn\l__gradbars_alt_fields_seq{/}{##1}
         \tl_put_right:Nx\l__gradbars_alt_tl
           {~Reference~\seq_item:Nn\l__gradbars_alt_fields_seq{4}:\c_space_tl
            \seq_item:Nn\l__gradbars_alt_fields_seq{1}.}}
      \tl_if_empty:oF {\gradbars@band}
        {\tl_put_right:Nx\l__gradbars_alt_tl {~Reference~band:~\gradbars@band.}}
      \ifgradbars@bullet
        \tl_put_right:Nn\l__gradbars_alt_tl {~Background~grade~upper~bounds:~}
        \clist_map_inline:Nn\l__gradbars_bands_clist
          {\seq_set_split:Nnn\l__gradbars_alt_fields_seq{/}{##1}
           \tl_put_right:Nx\l__gradbars_alt_tl
             {\seq_item:Nn\l__gradbars_alt_fields_seq{1};~}}
      \fi
    \fi
  }
\cs_new_protected:Npn \gradbarsApplyAlt
  {
    \cs_if_exist:NT \tag_if_active:T
      {\tag_if_active:T
        {\pgfkeysifdefined{/tikz/alt/.@cmd}
          {\str_case_e:nn {\gradbars@accessibility}
            {{auto}{\tl_if_empty:oF{\gradbars@alt}
                      {\tl_set:No\l__gradbars_alt_tl{\gradbars@alt}}
                    \tikzset{alt={\l__gradbars_alt_tl}}}
             {artifact}{\tikzset{artifact}}}}
          {}}}
  }
\cs_new_protected:Npn \gradbarsPrepareAlt
  {\cs_if_exist:NT\tag_if_active:T
     {\tag_if_active:T{\gradbarsBuildAlt\gradbarsApplyAlt}}}
\cs_new_protected:Npn \gradbarsPrepareLegendAlt
  {\cs_if_exist:NT\tag_if_active:T
     {\tag_if_active:T
       {\tl_set:Nx\l__gradbars_alt_tl
         {Legend~categories:~\clist_use:Nn\l__gradbars_names_clist{;~}.}
        \gradbarsApplyAlt}}}
\cs_new_protected:Npn \gradbarsSuspendMeasurementTagging
  {\cs_if_exist:NT\tag_suspend:n{\tag_suspend:n{gradbars-measurement}}}
\cs_new_protected:Npn \gradbarsResumeMeasurementTagging
  {\cs_if_exist:NT\tag_resume:n{\tag_resume:n{gradbars-measurement}}}
\ExplSyntaxOff
\endinput
