% \iffalse meta-comment
%<*internal>
\iffalse
%</internal>
%<*dummy>
I'd like to keep this part here for later use.
%</dummy>
%<*internal>
\fi
\def\nameofplainTeX{plain}
\ifx\fmtname\nameofplainTeX\else
  \expandafter\begingroup
\fi
%</internal>
%<*install>
\input l3docstrip.tex
\keepsilent
\askforoverwritefalse
\preamble
Copyright (C) 2016-2026, Ruini Xue <xueruini@gmail.com>

It may be distributed and/or modified under the conditions of
the LaTeX Project Public License (LPPL), either version 1.3c of
this license or (at your option) any later version.  The latest
version of this license is in the file:

   http://www.latex-project.org/lppl.txt
\endpreamble
\postamble

This work is "maintained" (as per LPPL maintenance status) by
  Ruini Xue.

This work consists of the file  zebra.dtx
          and the derived files zebra.ins,
                                zebra.pdf,
                                zebra.sty, and
                                zebra-goodies.sty.

\endpostamble
\usedir{tex/latex/zebra}
\generate{
  \file{\jobname.sty}{\from{\jobname.dtx}{package}}
  \file{\jobname-goodies.sty}{\from{\jobname.dtx}{compat}}
}
\nopreamble\nopostamble
\generate{
  \file{\jobname-demo-twocol.tex}{\from{\jobname.dtx}{demo-twocol}}
}
%</install>
%<install>\endbatchfile
%<*internal>
\usedir{source/latex/zebra}
\generate{
  \file{\jobname.ins}{\from{\jobname.dtx}{install}}
}
%\nopreamble\nopostamble
%\usedir{doc/latex/zebra}
%\generate{
%  \file{README.md}{\from{\jobname.dtx}{dummy}}
%}
\ifx\fmtname\nameofplainTeX
  \expandafter\endbatchfile
\else
  \expandafter\endgroup
\fi
%</internal>
%<*driver|package>
%\RequirePackage{xparse}
%</driver|package>
%<*driver>
\documentclass[full]{l3doc}
\usepackage[scaled=0.93]{helvet}
\usepackage[final]{listings}
\usepackage{fontawesome}
\usepackage{graphicx}
\graphicspath{{./out/}}
\usepackage{zebra}
\zebrasetup{symbol/fixed=\manerrarrow}
\colorlet{mycyan}{cyan!80!black}
\EnableCrossrefs
\CodelineIndex
\RecordChanges
\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</driver>
% \fi
%
%\makeatletter
%
%^^A For creating examples with nice highlighting of code, and so
%^^A on; based on the system used in the listings source (lstsample).
%\lst@RequireAspects{writefile}
%\newsavebox{\LaTeXdemo@box}
%\lstnewenvironment{LaTeXdemo}[1][code and example]{^^A
%  \global\let\lst@intname\@empty
%  \expandafter\let\expandafter\LaTeXdemo@end
%    \csname LaTeXdemo@#1@end\endcsname
%  \@nameuse{LaTeXdemo@#1}^^A
%}{^^A
%  \LaTeXdemo@end
%}
%\newcommand*\LaTeXdemo@new[3]{^^A
%  \expandafter\newcommand\expandafter*\expandafter
%    {\csname LaTeXdemo@#1\endcsname}{#2}^^A
%  \expandafter\newcommand\expandafter*\expandafter
%    {\csname LaTeXdemo@#1@end\endcsname}{#3}^^A
%}
%\newcommand*\LaTeXdemo@common{^^A
%  \setkeys{lst}{
%    basicstyle      = \small\ttfamily,
%    %basewidth      = 0.51em,
%    frame           = l,
%    backgroundcolor = \color{gray!5},
%    gobble          = 3,
%    keywordstyle    = \bfseries\color{blue},
%    language        = [LaTeX]{TeX},
%    moretexcs       = {
%      todo            ,
%      note            ,
%      comment         ,
%      fixed           ,
%      placeholder     ,
%      question        ,
%      zebratodo       ,
%      zebranote       ,
%      zebracomment    ,
%      zebrafixed      ,
%      zebraplaceholder,
%      zebranewnote    ,
%      zebraref        ,
%      zebrasetup      ,
%      colorlet
%    }
%  }^^A
%}
%\newcommand*\LaTeXdemo@input{^^A
%  \MakePercentComment
%  \small
%  \begingroup
%    \endlinechar=13\relax
%    \catcode`\^^M=5\relax
%    \input{\jobname.tmp}^^A
%  \endgroup
%  \MakePercentIgnore
%}
%\LaTeXdemo@new{code and example}{^^A
%  \setbox\LaTeXdemo@box=\hbox\bgroup
%    \lst@BeginAlsoWriteFile{\jobname.tmp}^^A
%    \LaTeXdemo@common
%}{^^A
%    \lst@EndWriteFile
%  \egroup
%  \begin{center}
%    \ifdim\wd\LaTeXdemo@box>0.48\linewidth\relax
%      \hbox to\linewidth{\box\LaTeXdemo@box\hss}^^A
%        \begin{minipage}{\linewidth}
%          \LaTeXdemo@input
%        \end{minipage}
%    \else
%      \begin{minipage}{0.48\linewidth}
%        \LaTeXdemo@input
%      \end{minipage}
%      \hfill
%      \begin{minipage}{0.48\linewidth}
%        \hbox to\linewidth{\box\LaTeXdemo@box\hss}^^A
%      \end{minipage}
%    \fi
%  \end{center}
%}
%\LaTeXdemo@new{code only}{^^A
%  \LaTeXdemo@common
%}{^^A
%}
%
%\providecommand*\opt[1]{\texttt{#1}}
%
%\makeatother
%
%\GetFileInfo{\jobname.sty}
%
%\changes{v0.1.0}{2017/07/05}{Initial public release}
%\changes{v0.2.0}{2017/11/26}{Fix \pkg{xcolor} conflict}
%\changes{v0.3.0}{2017/12/06}{Detect command conflicts}
%\changes{v0.4.0}{2019/06/29}{Show note number for easy reference}
%\changes{v0.5.0}{2019/06/29}{Use darker color for label}
%\changes{v0.6.0}{2019/06/30}{Use gray background for label}
%\changes{v0.9.0}{2026/02/25}{Fix legacy bugs and improve implementation}
%\changes{v0.9.1}{2026/02/27}{Beautify the numbers.}
%\changes{v0.9.2}{2026/03/03}{Faster.}
%\changes{v1.0.0}{2026/03/16}{\pkg{expl3}, list of notes and compatibility.}
%\changes{v1.1.0}{2026/03/21}{Customisable margin symbol, accurate page numbers, code cleanup.}
%\changes{v1.1.1}{2026/03/22}{Per-type \texttt{color}/\texttt{symbol} keys, \cs{zebrasetup}.}
%\changes{v1.2.0}{2026/03/23}{Simplify key architecture.}
%\changes{v1.3.0}{2026/03/25}{Rename package to \pkg{zebra}.}
%\changes{v1.3.0}{2026/03/25}{Rename the page-link option to \opt{pagelinks}/\opt{nopagelinks}.}
%\changes{v1.3.0}{2026/03/25}{Rename the microtype expansion option to \opt{font-expansion}.}
%\changes{v1.4.0}{2026/04/03}{Support note labels via \cs{label}, \cs{ref}, and \cs{zebraref}.}
%\changes{v1.5.0}{2026/04/14}{Fix notes numbering in moving arguments.}
%\changes{v1.6.0}{2026/04/16}{Numbering in moving arguments is hard.}
%\changes{v1.7.0}{2026/04/25}{Robust margin notes in twocolumn captions, titles and headings.}
%\changes{v1.8.0}{2026/04/25}{Fix margin symbol distance.}
%\changes{v1.8.0}{2026/04/26}{Add \opt{unnumbered} option for lightweight mode.}
%\changes{v1.8.0}{2026/04/26}{Stabilise labeled notes replayed through uppercased running heads.}
%\changes{v1.8.1}{2026/04/27}{Drop note bodies from PDF bookmarks.}
%\changes{v1.8.1}{2026/04/27}{Fix multiply-defined labels for repeated unlabeled notes.}
%\changes{v1.8.1}{2026/04/27}{Keep labeled notes with shared content distinct.}
%\changes{v1.8.1}{2026/04/29}{Silence pass-1 warning from \cs{zebraref}.}
%
%\title{^^A
%  \pkg{zebra} --- Writing Revision Toolkit^^A
%    \thanks{^^A
%      This package was previously distributed as \pkg{zebra-goodies}.
%      The old name still works but will print a deprecation warning. Please
%      update to \pkg{zebra}.^^A
%    }^^A
%}
%\author{^^A
%  Ruini Xue\thanks{Email: xueruini@gmail.com}^^A
%}
%\date{\fileversion~(\filedate)}
%
%\maketitle
%
%\begin{abstract}
%  The \pkg{zebra} package is a writing revision toolkit. The current release
%  focuses on inline note-taking, with a lightweight set of macros designed to
%  be simple and practical for both solo and collaborative workflows. Five
%  built-in commands---\cs{todo}, \cs{note}, \cs{comment}, \cs{fixed}, and
%  \cs{placeholder}---cover common use cases out of the box, and
%  \cs{zebranewnote} lets you define additional note types as needed. Notes are
%  automatically numbered per type, marked with a customisable
%  symbol\marginnote{\textdbend} (default: \cs{textdbend}) in the nearest
%  margin, and summarised with a summary table plus a detailed note list at the
%  end of the document. Passing the \opt{final} option suppresses all notes for
%  production output.
%\end{abstract}
%
%\tableofcontents
%
%\begin{documentation}
%
%\section{Introduction}
%
% \pkg{zebra} is a writing revision toolkit. The current release focuses on
% inline note-taking. Many note-taking and to-do packages exist for \LaTeX{},
% but most fall into one of two traps: they either offer an overwhelming
% feature set that tries to cover every conceivable use case, or they clutter
% the margins with oversized colourful boxes and arrows that make the document
% hard to read.
%
% \pkg{zebra} takes a different approach. It aims to be \emph{simple}
% ---intuitive commands with only the arguments you actually need---and
% \emph{good enough}---notes appear inline with a small visual cue in the
% margin, keeping the document readable while still making annotations easy to
% spot. Each note type is automatically numbered, and a summary table plus a
% detailed note list at the end of the document serve as a gentle reminder to
% address them before the final version.
%
%\section{Installation}
%
%\changes{v0.7.0}{2019/07/01}{Move to docstrip}
%
% \pkg{zebra} is available on CTAN; install it through your \LaTeX{}
% distribution's package manager. To build from source, run
% \texttt{latexmk \jobname.dtx} to extract the package and typeset the
% documentation in one step.
%
%\section{Using the package}
%
% Load the package in the preamble with any desired options.
%\begin{LaTeXdemo}[code only]
%  \usepackage[<options>]{zebra}  % was zebra-goodies
%\end{LaTeXdemo}
%
%\subsection{Package Options}
%\label{sec:options}
%
%\DescribeOption{draft}
%\DescribeOption{final}
% These two options are complementary. Default: \texttt{true} (draft mode). All
% notes are typeset inline and a summary table plus a detailed note list are
% appended at the end of the document. Setting \opt{final} (or |draft=false|)
% suppresses all notes and the generated lists, producing clean output ready for
% distribution.
%
%\DescribeOption{sort}
% Controls the order of the detailed note list printed at the end of the
% document. Default: \texttt{none} (document order).
% |sort=type| groups them by note type.
%
%\DescribeOption{pagelinks}
% Controls whether page numbers in the detailed note list are clickable.
% Default: \texttt{true}. Set |pagelinks=false| to disable these links. The
% complementary option \opt{nopagelinks} is also accepted.
%
%\DescribeOption{font-expansion}
% Controls \pkg{microtype} font expansion. This usually improves the
% appearance of the document. Disable it if it conflicts with your engine
% or another package by setting |font-expansion=false|. The complementary
% option \opt{nofont-expansion} is also accepted. Default: \texttt{true}.
% \pkg{microtype} remains loaded when expansion is disabled.
%
%\DescribeOption{unnumbered}
% Turns off note numbering. Inline markers become \texttt{[todo: \dots]}
% (no number), the margin symbol carries no number, the end-of-document
% detailed note list is omitted (the per-type summary table remains),
% and \cs{zebraref} falls back to \cs{ref}. Useful as a workaround if
% numbered notes interact badly with a particular class; the summary
% table count is then an upper-bound estimate. Default: \texttt{false}.
%
%\subsection{Notes Macros}
%
% All note commands share the syntax \cs{cmd}\oarg{name}\marg{text}. Each also
% has a prefixed alias (e.g.\ \cs{zebratodo}) that is always available,
% regardless of name conflicts. If a short name clashes with another loaded
% package, \pkg{zebra} will \emph{not} overwrite the existing
% definition; use the prefixed form instead.
%
%\begin{function}{\todo, \zebratodo}
%  \begin{syntax}
%    \cs{todo}\oarg{name}\marg{text}
%    \cs{zebratodo}\oarg{name}\marg{text}
%  \end{syntax}
%\end{function}
%
% The primary command provided by \pkg{zebra} is \cs{todo}. It inserts
% an inline note in the current paragraph, typeset in a predefined colour and
% marked with a symbol in the nearest margin. The mandatory \meta{text}
% describes the task; the optional \meta{name} specifies who is responsible for
% addressing it, which is particularly useful during collaborative writing.
%
% \begin{LaTeXdemo}
%   The motivation section still feels too vague \todo{revise the introduction
%   before submission} and could benefit from a concrete running example to
%   guide the reader through the key ideas step by step.
% \end{LaTeXdemo}
%
% The optional argument assigns one or more people to the note. Assignees appear
% prefixed with \texttt{@}, and notes of the same type are numbered
% sequentially.
%
% \begin{LaTeXdemo}
%   The related work section needs more references \todo[alice]{add two or
%   three citations from the latest survey} to recent advances in the field. We
%   should also double-check the experimental setup before the camera-ready
%   deadline \todo[bob, carol]{verify the hyperparameter table against the
%   source code and update any outdated entries and let's check afterwards}.
% \end{LaTeXdemo}

% Notes can appear inside moving arguments such as \cs{section} and
% \cs{caption}.  To ensure stable numbering and cross-references, add a
% \cs{label} inside the note:
%
% \begin{LaTeXdemo}[code only]
%   \section{Introduction\todo[jerry]{\label{zebra:heading}fix the name}}
%   \begin{figure}
%     \caption{Speed vs distance. \todo{need to insert the figure}}
%   \end{figure}
% \end{LaTeXdemo}
%
% With a \cs{label}, the note is counted once regardless of how many
% times the heading appears (table of contents, running headers, etc.).
% Notes without a \cs{label} in moving arguments are still safe but may
% receive a separate number in each context.
%
%\begin{function}{\note, \zebranote}
%  \begin{syntax}
%    \cs{note}\oarg{name}\marg{text}
%    \cs{zebranote}\oarg{name}\marg{text}
%  \end{syntax}
%\end{function}
%\begin{function}{\comment, \zebracomment}
%  \begin{syntax}
%    \cs{comment}\oarg{name}\marg{text}
%    \cs{zebracomment}\oarg{name}\marg{text}
%  \end{syntax}
%\end{function}
%\begin{function}{\fixed, \zebrafixed}
%  \begin{syntax}
%    \cs{fixed}\oarg{name}\marg{text}
%    \cs{zebrafixed}\oarg{name}\marg{text}
%  \end{syntax}
%\end{function}
%\begin{function}{\placeholder, \zebraplaceholder}
%  \begin{syntax}
%    \cs{placeholder}\oarg{name}\marg{text}
%    \cs{zebraplaceholder}\oarg{name}\marg{text}
%  \end{syntax}
%\end{function}
% These commands share the same syntax and behaviour as \cs{todo}; they differ
% only in name and colour, providing semantic distinction for different
% annotation purposes. Note that \cs{zebracomment} is used in the example below
% because \cs{comment} is already defined by \pkg{l3doc}.
% \begin{LaTeXdemo}
%   We may want to reorganise \note{how should we structure the intro?} this
%   part before the final submission. The experimental setup in Section~2 has
%   already been reviewed by a collaborator \zebracomment[tom]{the setup
%   description looks clear now}. Results are presented in the following tables
%   and figures, but some of them are still missing.
%
%   The discussion has been revised \placeholder[lucy, tom]{good job!}
%   and the related work comparison strengthened with two additional references.
%   The list of references still needs a second pass \todo{check bibliography
%   entries for formatting} before we can finalize the submission.
%
%   With those items addressed, the conclusion has been rewritten so the
%   argument flows more naturally from the results. \fixed[John]{updated the
%   conclusion} The overall structure now matches the revised outline we agreed
%   on last week. \note[who]{anything else?} If not, the draft should be fine.
% \end{LaTeXdemo}
%
%\begin{function}{\zebranewnote}
%  \begin{syntax}
%    \cs{zebranewnote}\marg{note name}\marg{xcolor name}\oarg{symbol}
%  \end{syntax}
%\end{function}
% Creates a new note type. The \meta{note name} becomes the command name
% (e.g.\ passing |question| creates \cs{question} and \cs{zebraquestion}), and
% \meta{xcolor name} sets its colour. The colour must be a named colour already
% known to \pkg{xcolor}; define it with \cs{definecolor} or \cs{colorlet}
% beforehand if needed. The optional \meta{symbol} overrides the default margin
% symbol (\cs{textdbend}) for this note type only. Per-type symbols can also be
% changed after loading via
% |\zebrasetup{symbol/|\meta{type}|=|\meta{symbol}|}|.
% \changes{v0.8.0}{2019/07/04}{Fix new note demo}
% \changes{v0.8.1}{2019/07/04}{Fix doc}
% \begin{LaTeXdemo}
%   \colorlet{mycyan}{cyan!80!black}
%   \zebranewnote{question}{mycyan}[\faQuestionCircle] % \usepackage{fontawesome}
%
%   When it moves to the next step, we should be fine.\question[who]{what's this?}
% \end{LaTeXdemo}
%
%\begin{function}{\zebraref}
%  \begin{syntax}
%    \cs{zebraref}\marg{label}
%  \end{syntax}
%\end{function}
%
% Labels may be placed inside note bodies with the usual \cs{label} command.
% Standard \cs{ref} returns the note number, while \cs{zebraref} prints the
% note type together with the number.
% \begin{LaTeXdemo}
%   The motivation section still feels too vague \todo{\label{zebra:intro}revise
%   the introduction before submission}.  The same issue appears again later
%   \note{see Todo~\ref{zebra:intro} (that is, \zebraref{zebra:intro}) on
%   p.~\pageref{zebra:intro}}.
% \end{LaTeXdemo}
%
% As in standard \LaTeX{}, labels inside notes are unavailable in
% \opt{final} mode because the notes themselves are suppressed.
%
%\begin{function}{\zebrasetup}
%  \begin{syntax}
%    \cs{zebrasetup}\marg{key\,=\,value list}
%  \end{syntax}
%\end{function}
% Configures note appearance after loading. Accepted keys:
% \begin{itemize}
%   \item |color/|\meta{type}|=|\meta{colour} --- override the
%     colour of a note type.
%   \item |symbol/|\meta{type}|=|\meta{symbol} --- override the
%     margin symbol of a note type.
% \end{itemize}
% For example:
% \begin{LaTeXdemo}[code only]
%   \zebrasetup{symbol/fixed=\manerrarrow} % like this doc
%   \zebrasetup{color/todo=red}
% \end{LaTeXdemo}
%
%\subsection{Two-column Support}
%
% In \texttt{twocolumn} documents, the margin symbol is automatically placed on
% the nearest margin: left margin for the left column, right margin for the
% right column. No special configuration is needed. This also works correctly in
% combination with the \texttt{twoside} option.
% \lstinputlisting[
%   basicstyle=\small\ttfamily,
%   frame=l,
%   backgroundcolor=\color{gray!5},
%   firstline=3, ^^A docstrip adds 2 more lines
%   keywordstyle=\bfseries\color{blue},
%   language={[LaTeX]TeX},
%   moretexcs={todo,note,comment,fixed,placeholder,zebrasetup},
% ]{\jobname-demo-twocol.tex}
%
% The code above produces the following output:
%
% \begin{center}
%   \fbox{\includegraphics[width=\linewidth,page=1]{zebra-demo-twocol}}
% \end{center}
%
%
%
%\end{documentation}
%
%\begin{implementation}
%
%\section{Implementation}
%
%    \begin{macrocode}
%<*package>
%    \end{macrocode}
%
%    \begin{macrocode}
%<@@=zebra>
%    \end{macrocode}
%
% Version data to start with.
%    \begin{macrocode}
\ProvidesExplPackage{zebra}
  {2026/04/29}
  {1.8.1}
  {Writing Revision Toolkit}
%    \end{macrocode}
%
%\subsection{Package options}
%
% Package options \opt{draft}, \opt{pagelinks}, \opt{font-expansion},
% \opt{sort}, and \opt{unnumbered} are created using
% the kernel key--value interface available since \LaTeX{} 2022-06-01.
% Post-load configuration (\cs{zebrasetup}) uses a separate
% \texttt{zebra-setup} key family with \texttt{color/\meta{type}} and
% \texttt{symbol/\meta{type}} sub-families.
%    \begin{macrocode}
\bool_new:N \l@@_draft_bool
\bool_new:N \l@@_emit_label_bool
\bool_new:N \l@@_microtype_expansion_bool
\bool_new:N \l@@_pagelinks_bool
\bool_new:N \l@@_sort_none_bool
\bool_new:N \l@@_unnumbered_bool
\seq_new:N \g@@_note_types_seq
\prop_new:N \g@@_note_colors_prop
\prop_new:N \g@@_note_public_alias_prop
\int_new:N \g@@_note_id_int
\tl_new:N \l@@_note_target_tl
\tl_new:N \l@@_note_color_tl
\tl_new:N \l@@_note_ref_type_tl
\tl_new:N \l@@_summary_rows_tl
\int_new:N \l@@_total_notes_int
\prop_new:N \g@@_note_symbols_prop
\tl_new:N \l@@_symbol_tl
\tl_set:Nn \l@@_symbol_tl { \textdbend }

\msg_new:nnn { zebra } { command-taken }
  {
    The~command~'\iow_char:N\\#1'~is~already~defined.~
    Use~'\iow_char:N\\zebra#1'~instead.
  }
\msg_new:nnn { zebra } { duplicate-note-label }
  { Note~label~'#1'~used~by~a~different~note;~second~note~gets~
    its~own~identity. }

\prg_new_conditional:Npnn \@@_if_package_loaded:n #1 { T , F , TF }
  {
    \cs_if_exist:cTF { ver@#1.sty }
      { \prg_return_true: }
      { \prg_return_false: }
  }

\keys_define:nn { zebra }
  {
    draft .bool_set:N = \l@@_draft_bool,
    draft .initial:n = true,
    final .meta:n = { draft = false },
    font-expansion .bool_set:N = \l@@_microtype_expansion_bool,
    font-expansion .initial:n = true,
    nofont-expansion .meta:n = { font-expansion = false },
    pagelinks .bool_set:N = \l@@_pagelinks_bool,
    pagelinks .initial:n = true,
    nopagelinks .meta:n = { pagelinks = false },
    sort .choice:,
    sort / type .code:n = { \bool_set_false:N \l@@_sort_none_bool },
    sort / none .code:n = { \bool_set_true:N \l@@_sort_none_bool },
    sort .initial:n = none,
    unnumbered .bool_set:N = \l@@_unnumbered_bool,
    unnumbered .initial:n = false,
  }
\ProcessKeyOptions [ zebra ]
\keys_define:nn { zebra-setup / color }
  {
    unknown .code:n =
      { \prop_gput:NVn \g@@_note_colors_prop \l_keys_key_str {#1} }
  }
\keys_define:nn { zebra-setup / symbol }
  {
    unknown .code:n =
      { \prop_gput:NVn \g@@_note_symbols_prop \l_keys_key_str {#1} }
  }
\sys_if_engine_xetex:T
  { \bool_set_false:N \l@@_microtype_expansion_bool }

\cs_new_protected:Npn \@@_setup_microtype:
  {
    \@@_if_package_loaded:nTF { microtype }
      {
        \bool_if:NF \l@@_microtype_expansion_bool
          { \microtypesetup { expansion = false } }
      }
      {
        \bool_if:NTF \l@@_microtype_expansion_bool
          { \RequirePackage{microtype} }
          { \RequirePackage[expansion=false]{microtype} }
      }
  }
\@@_setup_microtype:
\@@_if_package_loaded:nF { hyperref }
  {
    \bool_if:NT \l@@_pagelinks_bool
      {
        \RequirePackage{hyperref}
        \hypersetup { pdfborder = { 0~0~0 } }
      }
  }
%    \end{macrocode}
%
% \subsection{Moving-argument deduplication}
%
% Notes inside moving arguments (\cs{section}, \cs{caption}, etc.) may be
% processed more than once per compilation pass.  Two separate problems
% are handled independently:
%
% \paragraph{Problem A --- \cs{sbox} re-measurement.}
% \cs{@makecaption} typesets the caption in an \cs{sbox} for width
% measurement, then typesets it again if it is long.  Both executions
% share the same \cs{inputlineno}, so the \emph{instance key}
% (\meta{type}\verb"|"\meta{author}\verb"|"\meta{body}\verb"|"\cs{inputlineno})
% catches the replay for both labeled and unlabeled notes.  The second
% execution reuses the first's allocation and re-renders, so writes
% that were lost inside the discarded \cs{sbox} are re-emitted by the
% actual typesetting pass.
%
% \paragraph{Problem B --- TOC/LOF/header replay.}
% The note token is written verbatim to \texttt{.toc}/\texttt{.lof}/marks
% and re-executed in a secondary context with a different
% \cs{inputlineno}.  For \emph{labeled} notes the replay is caught by
% two mechanisms:
% \begin{enumerate}
%   \item A \emph{stable key} (\meta{type}\verb"|"\meta{label name})
%     stored alongside the allocation; a later encounter from
%     marks/headers that still carries the \cs{label} finds this key
%     and suppresses.
%   \item A \emph{content signature}
%     (\meta{type}\verb"|"\meta{author}\verb"|"\meta{sanitised body})
%     written to the \texttt{.aux} file; on the next pass, TOC/LOF
%     encounters whose \cs{label} was consumed by
%     \cs{protected@write}'s \cs{edef} match the signature and
%     suppress.
% \end{enumerate}
% Unlabeled notes in moving arguments receive independent allocations
% on each replay (cosmetic duplicate on TOC/LOF/marks); adding
% \cs{label} is the recommended fix when stable replay behaviour is
% required.
%
% \paragraph{Limitation --- same-line same-body unlabeled.}
% Two unlabeled notes with identical body on the same source line
% (e.g.\ \cs{todo}\verb"{x}"\cs{todo}\verb"{x}") share an instance key
% with a single \cs{sbox} replay of the same source location and are
% indistinguishable from it at the TeX level.  Case~1 collapses the
% second occurrence into the first.  Workarounds: split across lines,
% vary the body, or add \cs{label}.  Notes inside \cs{fbox},
% \texttt{tabular} cells, \cs{parbox}, \texttt{minipage}, footnotes,
% and other inner-mode containers are unaffected --- a single source
% encounter records normally regardless of the surrounding mode.
%
%    \begin{macrocode}
\RequirePackage{xcolor}
\RequirePackage{marginnote}
\cs_new_eq:NN \@@_kernel_label:n \label
%% -- dedup data structures --
%% Maps any key (instance, stable, or content-sig) to the allocation.
\prop_new:N \g@@_note_target_prop
\prop_new:N \g@@_note_display_prop
%% Content signatures of labeled notes from previous pass (.aux).
\prop_new:N \g@@_note_sig_known_prop
%% Content signatures written this pass (dedup aux writes).
\prop_new:N \g@@_note_sig_written_prop
%% Content signature stored per stable key (for label-conflict detection).
\prop_new:N \g@@_note_stable_sig_prop
%% Group-local set of targets whose \label has been emitted.  Sbox
%% boundaries reset it (so caption replay re-emits after the sbox is
%% discarded), while a same-source-line repeat in body text sees the
%% prior emission and skips the redundant \label, avoiding
%% multiply-defined labels.
\prop_new:N \l@@_label_emitted_prop
\tl_new:N \l@@_note_display_tl
\tl_new:N \l@@_note_key_tl
%% Instance key: unique per source location.
\cs_new:Npn \@@_instance_key:nnn #1#2#3
  {
    \tl_to_str:n {#1}
    | \tl_to_str:n {#2}
    | \tl_to_str:n {#3}
    | \int_eval:n { \tex_inputlineno:D }
  }
%% Content signature: body stringified with ALL \label{...} stripped.
%% Matches across body (has labels), TOC (labels consumed by \edef),
%% and running heads (which may uppercase the author/body text).
%% The optional \protect prefix covers marks and \protected@write paths.
%% Uses replace_all so that multiple labels are all stripped.
\cs_new_protected:Npn \@@_content_sig:nnnN #1#2#3#4
  {
    \tl_set:Nx \l_tmpa_tl { \tl_to_str:n {#3} }
    \regex_replace_all:nnN
      { (?: \\protect \s* )? \\label \s* \{ [^{}]* \} } { } \l_tmpa_tl
    \tl_set:Nx #4
      {
        \tl_to_str:n {#1}
        | \str_lowercase:f { \tl_to_str:n {#2} }
        | \str_lowercase:f { \l_tmpa_tl }
      }
  }
%% Extract the last \label name from the stringified body
%% (the greedy \A.* consumes up to the rightmost \label).  Both the
%% original encounter and any replay run through this same regex,
%% so they agree on which label to use as the stable key.
%% Sets #2 to the label name, or clears it if none found.
%% The optional \protect prefix covers marks and \protected@write paths.
\cs_new_protected:Npn \@@_extract_label:nN #1#2
  {
    \tl_set:Nx \l_tmpb_tl { \tl_to_str:n {#1} }
    \tl_set_eq:NN \l_tmpc_tl \l_tmpb_tl
    \regex_replace_once:nnN
      { \A .* (?: \\protect \s* )? \\label \s* \{ ([^{}]*) \} .* \Z }
      { \1 } \l_tmpb_tl
    \tl_if_eq:NNTF \l_tmpb_tl \l_tmpc_tl
      { \tl_clear:N #2 }
      { \tl_set_eq:NN #2 \l_tmpb_tl }
  }
%% Stable key for labeled notes.
%% #2 is expected to be already stringified (from regex extraction),
%% so no \tl_to_str is applied — otherwise an unexpanded variable
%% token would be stringified instead of its value.
\cs_new:Npn \@@_stable_key:nn #1#2
  { \tl_to_str:n {#1} | label | #2 }
%% Allocate a fresh note: increment the type counter, generate
%% a unique hypertarget name, and record the note in the list body.
\cs_new_protected:Npn \@@_allocate_note:nnn #1#2#3
  {
    \int_gincr:c { g@@_note_count_#1_int }
    \tl_set:Nx \l@@_note_display_tl { \@@_note_count:n {#1} }
    \int_gincr:N \g@@_note_id_int
    \tl_set:Nx \l@@_note_target_tl
      { zebranote.\int_use:N \g@@_note_id_int }
    \@@_record_note:nnnnn
      {#1}
      { \l@@_note_display_tl }
      {#2}
      {#3}
      { \l@@_note_target_tl }
  }
%% Aux-file interface: record a content signature together with the
%% originating instance key.  A later encounter whose instance key
%% differs from the stored one is a replay and is suppressed.
%% Re-stringify for catcode normalisation.
\cs_new_protected:Npn \zebra@sig #1#2
  {
    \tl_set:Nx \l_tmpa_tl { \tl_to_str:n {#1} }
    \tl_set:Nx \l_tmpb_tl { \tl_to_str:n {#2} }
    \prop_gput:NVV \g@@_note_sig_known_prop \l_tmpa_tl \l_tmpb_tl
  }
\cs_new_protected:Npn \@@_write_sig:NN #1#2
  {
    \prop_if_in:NVF \g@@_note_sig_written_prop #1
      {
        \immediate\write \@auxout
          { \string\zebra@sig { \tl_use:N #1 } { \tl_use:N #2 } }
        \prop_gput:NVn \g@@_note_sig_written_prop #1 { 1 }
      }
  }
\cs_if_exist:NTF \dbend
  {
    \cs_set_eq:NN \@@_saved_dbend: \dbend
    \cs_undefine:N \dbend
    \RequirePackage{manfnt}
    \cs_set_eq:NN \dbend \@@_saved_dbend:
  }
  { \RequirePackage{manfnt} }
\cs_new:Npn \@@_pdfstring_note:
  #1
  {
    \str_if_eq:eeT { \tl_to_str:n {#1} } { [ }
      { \@@_pdfstring_note_opt:w }
  }
\cs_new:Npn \@@_pdfstring_note_opt:w #1 ] #2 { }
\cs_new:Npn \@@_target:nn #1#2 {#2}
\cs_new:Npn \@@_link:nn #1#2 {#2}
\cs_new:Npn \@@_pageref:n #1 { \pageref {#1} }
\cs_new:Npn \@@_zebra_label_name:n #1 { #1@zebra }
\cs_new:Npn \@@_zebra_label_type:n #1
  {
    \exp_after:wN \use_i:nn
      \cs:w r@\@@_zebra_label_name:n {#1}\cs_end:
      { }
  }
\cs_new_protected:Npn \@@_write_zebra_label:n #1
  {
    \protected@write \@auxout { }
      {
        \string\newlabel{\@@_zebra_label_name:n {#1}}
          {{\exp_not:V \l@@_note_ref_type_tl}{}}
      }
  }
\cs_new_protected:Npn \@@_note_label:n #1
  {
    \@@_kernel_label:n {#1}
    \@@_write_zebra_label:n {#1}
  }
\cs_new_protected:Npn \@@_zebra_ref:n #1
  {
    \cs_if_exist:cTF { r@\@@_zebra_label_name:n {#1} }
      { \@@_zebra_label_type:n {#1}~\ref{#1} }
      { \ref{#1} }
  }
\NewDocumentCommand \zebraref { m }
  {
    \bool_if:NTF \l@@_unnumbered_bool
      { \ref{#1} }
      { \@@_zebra_ref:n {#1} }
  }
\cs_new_protected:Npn \@@_apply_pdfstring_defs:
  {
    \pdfstringdefDisableCommands
      {
        \cs_set:Npn \zebraref ##1 { \ref{##1} }
        \seq_map_inline:Nn \g@@_note_types_seq
          {
            \cs_set_eq:cN { zebra##1 } \@@_pdfstring_note:
            \prop_if_in:NnT \g@@_note_public_alias_prop { ##1 }
              { \cs_set_eq:cN { ##1 } \@@_pdfstring_note: }
          }
      }
  }
\cs_new_protected:Npn \@@_setup_pagelinks:
  {
    \cs_set:Npn \@@_target:nn ##1##2 {##2}
    \cs_set:Npn \@@_link:nn ##1##2 {##2}
    \cs_set:Npn \@@_pageref:n ##1 { \pageref {##1} }
    \@@_if_package_loaded:nT { hyperref }
      {
        \cs_set:Npn \@@_pageref:n ##1 { \pageref* {##1} }
        \bool_if:NT \l@@_pagelinks_bool
          {
            \cs_set:Npn \@@_target:nn ##1##2 { \hypertarget{##1}{##2} }
            \cs_set:Npn \@@_link:nn ##1##2 { \hyperlink{##1}{##2} }
          }
        \@@_apply_pdfstring_defs:
      }
  }
\hook_gput_code:nnn { begindocument } { zebra }
  { \@@_setup_pagelinks: }
%    \end{macrocode}
%
% \subsection{Main notes macros}
% Various helper macros are defined before reaching out to
% the \cs{todo} commands.
%
% Place the margin note on the nearest margin. Takes two arguments:
% |#1| for the left margin (number then symbol) and |#2| for the right
% margin (symbol then number), so the symbol always sits closest to the
% text column. In single-column mode \cs{marginnote} is used with the
% right-margin variant as default. In twocolumn mode \cs{marginpar}
% positions the symbol close to the text column reliably (it picks
% side from \cs{if@firstcolumn}, not from a \texttt{.aux} round trip),
% so we keep that path for the body. We must, however, route around
% the contexts where \cs{marginpar} would crash with ``Float(s)
% lost'': caption sboxes (inner mode), caption parboxes (signalled by
% \cs{@captype}), and the wide \cs{vbox} that \cs{@topnewpage} builds
% for \cs{title} (signalled by \cs{hsize}${}>{}$\cs{columnwidth}).
% Those contexts fall back to \cs{marginnote}, where we additionally
% replicate \cs{@marginparreset} (\cs{@parboxrestore} +
% \cs{normalfont}\cs{normalsize}) so the marker does not inherit a
% \cs{Huge} font from \cs{title}.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_margin_note:nn #1#2
  {
    \legacy_if:nTF { @twocolumn }
      {
        \bool_lazy_or:nnTF
          { \mode_if_inner_p: }
          {
            \bool_lazy_or_p:nn
              { \cs_if_exist_p:N \@captype }
              { \dim_compare_p:nNn { \hsize } > { \columnwidth } }
          }
          {
            \marginnote
              [ { \@parboxrestore \normalfont \normalsize #1 } ]
              { \@parboxrestore \normalfont \normalsize #2 }
          }
          {
            \marginpar
              [ { \makebox[\marginparwidth][r]{#1} } ]
              { \makebox[\marginparwidth][l]{#2} }
          }
      }
      { \marginnote[#1]{#2} }
  }
\cs_new:Npn \@@_prepend:nn #1#2
  { \tl_if_blank:nTF {#2} {} {#1#2} }
\cs_new:Npn \@@_capitalize_type:n #1
  { \text_uppercase:n { \tl_head:n {#1} } \tl_tail:n {#1} }
\cs_new:Npn \@@_note_count:n #1
  { \int_use:c { g@@_note_count_#1_int } }
\cs_new:Npn \@@_note_color:n #1
  { \prop_item:Nn \g@@_note_colors_prop {#1} }
\cs_new:Npn \@@_note_symbol:n #1
  {
    \prop_if_in:NnTF \g@@_note_symbols_prop {#1}
      { \prop_item:Nn \g@@_note_symbols_prop {#1} }
      { \l@@_symbol_tl }
  }
\cs_new_protected:Npn \@@_new_listbody:n #1
  { \tl_new:c { g@@_listbody_#1_tl } }
\tl_new:N \g@@_listbody_all_tl
\cs_new:Npn \@@_use_listbody:n #1
  { \tl_use:c { g@@_listbody_#1_tl } }
\cs_new_protected:Npn \@@_record_note:nnnnn #1#2#3#4#5
  {
    \tl_gput_right:cx
      {
        \bool_if:NTF \l@@_sort_none_bool
          { g@@_listbody_all_tl }
          { g@@_listbody_#1_tl }
      }
      {
        \exp_not:N \@@_list_entry:nnnnn
        { \exp_not:n {#1} }
        {#2}
        { \exp_not:n {#3} }
        { \exp_not:n {#4} }
        {#5}
      }
  }
%% \@@_note_unnumbered:nnn {type}{author}{body}
%% Fast path for the \opt{unnumbered} option: skip dedup, .aux
%% signatures and list registration entirely.  The counter is still
%% bumped on every call (so the summary table reflects rough counts;
%% notes inside captions/TOC/marks may be over-counted, which is the
%% accepted trade-off for cutting out the fragile bookkeeping).
\cs_new_protected:Npn \@@_note_unnumbered:nnn #1#2#3
  {
    \bool_if:NT \l@@_draft_bool
      {
        \tl_set:Nx \l@@_note_color_tl { \@@_note_color:n {#1} }
        \int_gincr:c { g@@_note_count_#1_int }
        \@@_render_note:nnn {#1} {#2} {#3}
      }
  }
%% \@@_note:nnn {type}{author}{body}
%% Main entry point: dispatch to the lightweight unnumbered path or to
%% the numbered path with deduplication/bookkeeping.
\cs_new_protected:Npn \@@_note:nnn #1#2#3
  {
    \bool_if:NTF \l@@_unnumbered_bool
      { \@@_note_unnumbered:nnn {#1} {#2} {#3} }
      { \@@_note_numbered:nnn {#1} {#2} {#3} }
  }
%% Numbered path.  Four cases:
%%   Case 1 — sbox reuse:   instance_key found → reuse, render
%%   Case 2 — stable key:   label found, stable_key in prop → suppress
%%   Case 3 — content sig:  sig in .aux data → suppress
%%   Case 4 — new note:     allocate, render
%% Case 1 also catches an unlabeled same-source-line same-body pair
%% (\todo{x}\todo{x}); the second is collapsed.  Two such notes are
%% indistinguishable at TeX level from a single note replayed by an
%% sbox/moving argument, so this collision is documented and resolved
%% by adding \label or splitting the line.
\cs_new_protected:Npn \@@_note_numbered:nnn #1#2#3
  {
    \bool_if:NT \l@@_draft_bool
      {
        \tl_set:Nx \l@@_note_color_tl { \@@_note_color:n {#1} }
        %% Case 1: sbox reuse (same \inputlineno)
        \tl_set:Nx \l@@_note_key_tl
          { \@@_instance_key:nnn {#1} {#2} {#3} }
        \prop_get:NVNTF \g@@_note_target_prop \l@@_note_key_tl
            \l@@_note_target_tl
          {
            \prop_get:NVN \g@@_note_display_prop \l@@_note_key_tl
              \l@@_note_display_tl
            \@@_render_note:nnn {#1} {#2} {#3}
          }
          {
            %% Extract label and compute content signature
            \@@_extract_label:nN {#3} \l_tmpb_tl
            \@@_content_sig:nnnN {#1} {#2} {#3} \l_tmpa_tl
            %% Case 2: stable-key suppress (labeled, marks/headers).
            %% If the stable key exists AND the content signature
            %% matches, this encounter is a replay → suppress.
            %% Different content signature = label reuse → warn and
            %% let Case 4 allocate independently.
            \bool_set_false:N \l_tmpa_bool
            \tl_if_empty:NF \l_tmpb_tl
              {
                \tl_set:Nx \l@@_note_key_tl
                  { \@@_stable_key:nn {#1} { \l_tmpb_tl } }
                \prop_get:NVNT \g@@_note_stable_sig_prop
                    \l@@_note_key_tl \l_tmpc_tl
                  {
                    \tl_if_eq:NNTF \l_tmpa_tl \l_tmpc_tl
                      { \bool_set_true:N \l_tmpa_bool }
                      {
                        \msg_warning:nnV { zebra }
                          { duplicate-note-label } \l_tmpb_tl
                      }
                  }
              }
            %% Case 3: content-sig suppress (TOC/LOF/marks replay).
            %% Only unlabeled current encounters may be suppressed by
            %% content signature; labeled encounters already have a
            %% strong identity via the stable key.  Suppress only if
            %% the stored instance key differs from the current one —
            %% same key means it is the original note, not a replay.
            \bool_if:NF \l_tmpa_bool
              {
                \tl_if_empty:NT \l_tmpb_tl
                  {
                    \prop_get:NVNT \g@@_note_sig_known_prop
                        \l_tmpa_tl \l_tmpc_tl
                      {
                        \tl_set:Nx \l_tmpd_tl
                          { \@@_instance_key:nnn {#1} {#2} {#3} }
                        \tl_if_eq:NNF \l_tmpc_tl \l_tmpd_tl
                          { \bool_set_true:N \l_tmpa_bool }
                      }
                  }
              }
            \bool_if:NF \l_tmpa_bool
              {
                %% Case 4: new note — allocate and render
                \tl_set:Nx \l@@_note_key_tl
                  { \@@_instance_key:nnn {#1} {#2} {#3} }
                \@@_allocate_note:nnn {#1} {#2} {#3}
                \prop_gput:NVV \g@@_note_target_prop
                  \l@@_note_key_tl \l@@_note_target_tl
                \prop_gput:NVV \g@@_note_display_prop
                  \l@@_note_key_tl \l@@_note_display_tl
                %% For labeled notes: register stable key + write sig,
                %% but only if the stable key is not already claimed by
                %% an earlier note (label-conflict case).
                \tl_if_empty:NF \l_tmpb_tl
                  {
                    \tl_set:Nx \l@@_note_key_tl
                      { \@@_stable_key:nn {#1} { \l_tmpb_tl } }
                    \prop_if_in:NVF \g@@_note_stable_sig_prop
                        \l@@_note_key_tl
                      {
                        \prop_gput:NVV \g@@_note_target_prop
                          \l@@_note_key_tl \l@@_note_target_tl
                        \prop_gput:NVV \g@@_note_display_prop
                          \l@@_note_key_tl \l@@_note_display_tl
                        \prop_gput:NVV \g@@_note_stable_sig_prop
                          \l@@_note_key_tl \l_tmpa_tl
                        \tl_set:Nx \l_tmpc_tl
                          { \@@_instance_key:nnn {#1} {#2} {#3} }
                        \@@_write_sig:NN \l_tmpa_tl \l_tmpc_tl
                      }
                  }
                \@@_render_note:nnn {#1} {#2} {#3}
              }
            %% Cases 2–3: suppress — no output
          }
      }
  }
%% Full render: hypertarget, target label, margin note, inline text.
%% In \opt{unnumbered} mode the label/hypertarget setup is skipped so a
%% \cs{label} inside the note body falls through to the surrounding
%% \cs{@currentlabel} (e.g.\ the section number), and \cs{zebraref}
%% degrades cleanly to \cs{ref}.
\cs_new_protected:Npn \@@_render_note:nnn #1#2#3
  {
    %% Decide outside render's group whether to emit the kernel label.
    %% The flag lives in the caller's group: sbox/parbox boundaries reset
    %% it (so caption replay re-emits), but a same-line repeat in body
    %% text sees the prior emission and skips the redundant \label.
    \bool_set_false:N \l@@_emit_label_bool
    \bool_if:NF \l@@_unnumbered_bool
      {
        \prop_if_in:NVF \l@@_label_emitted_prop \l@@_note_target_tl
          {
            \bool_set_true:N \l@@_emit_label_bool
            \prop_put:NVn \l@@_label_emitted_prop
              \l@@_note_target_tl { 1 }
          }
      }
    \group_begin:
      \bool_if:NF \l@@_unnumbered_bool
        {
          \protected@edef \@currentlabel { \l@@_note_display_tl }
          \@@_if_package_loaded:nT { hyperref }
            { \tl_set:Nx \@currentHref { \l@@_note_target_tl } }
          \tl_set:Nx \l@@_note_ref_type_tl
            { \@@_capitalize_type:n {#1} }
          \@@_target:nn { \l@@_note_target_tl } {}
          \bool_if:NT \l@@_emit_label_bool
            { \exp_args:NV \@@_kernel_label:n \l@@_note_target_tl }
        }
      \@@_margin_note:nn
        {\textcolor{\l@@_note_color_tl}{%
          \bool_if:NF \l@@_unnumbered_bool
            { {\bfseries\l@@_note_display_tl}\kern1pt }%
          \@@_note_symbol:n {#1}}}
        {\textcolor{\l@@_note_color_tl}{%
          \@@_note_symbol:n {#1}%
          \bool_if:NF \l@@_unnumbered_bool
            { \kern1pt {\bfseries\l@@_note_display_tl} }}}%
      \bool_if:NF \l@@_unnumbered_bool
        { \cs_set_eq:NN \label \@@_note_label:n }
      \textcolor{\l@@_note_color_tl}{[\colorbox[gray]{0.97}{%
          \textcolor{\l@@_note_color_tl !70!black}{%
            \textsc{\MakeLowercase{\MakeUppercase#1}}%
            \bool_if:NF \l@@_unnumbered_bool
              { ~\l@@_note_display_tl }%
            \texttt{\@@_prepend:nn {@}{#2}}:}} #3]}%
    \group_end:
  }
\cs_new_protected:Npn \@@_new_note_type:nn #1#2
  { \@@_new_note_type:nnn {#1} {#2} {} }
\cs_new_protected:Npn \@@_new_note_type:nnn #1#2#3
  {
    \seq_gput_right:Nn \g@@_note_types_seq {#1}
    \prop_if_in:NnF \g@@_note_colors_prop {#1}
      { \prop_gput:Nnn \g@@_note_colors_prop {#1} {#2} }
    \tl_if_blank:nF {#3}
      {
        \prop_if_in:NnF \g@@_note_symbols_prop {#1}
          { \prop_gput:Nnn \g@@_note_symbols_prop {#1} {#3} }
      }
    \int_new:c { g@@_note_count_#1_int }
    \@@_new_listbody:n {#1}
    \exp_args:Nc \NewDocumentCommand { zebra#1 } { O{} m }
      { \@@_note:nnn {#1}{##1}{##2} }
    \cs_if_exist:cTF {#1}
      { \msg_warning:nnn { zebra } { command-taken } {#1} }
      {
        \cs_set_eq:cc {#1} {zebra#1}
        \prop_gput:Nnn \g@@_note_public_alias_prop {#1} { true }
      }
    \@@_if_package_loaded:nT { hyperref }
      { \@@_apply_pdfstring_defs: }
  }
\cs_new_protected:Npn \@@_list_entry:nnnnn #1#2#3#4#5
  {
    \par\noindent
    \textcolor{\@@_note_color:n {#1}}{%
      \textbf{\@@_capitalize_type:n {#1}~#2}%
      \tl_if_blank:nF {#3} { \enspace \texttt{\@@_prepend:nn {@}{#3}} }}%
    \nobreak\dotfill
    \@@_link:nn {#5} { \@@_pageref:n {#5} }%
    \par
    \begingroup
      \leftskip=2em
      \rightskip=2em
      \parindent=0pt
      \cs_set_eq:NN \label \use_none:n
      #4\par
    \endgroup
  }
\cs_new_protected:Npn \@@_print_note_group:n #1
  {
    \int_compare:nNnT { \@@_note_count:n {#1} } > { 0 }
      {
        \par\medskip
        \@@_use_listbody:n {#1}
      }
  }
\cs_new_protected:Npn \@@_print_notes_inorder:
  {
    \tl_if_empty:NF \g@@_listbody_all_tl
      { \par\medskip \tl_use:N \g@@_listbody_all_tl }
  }
\cs_new_protected:Npn \@@_summary_row:n #1
  {
    \int_compare:nNnT { \@@_note_count:n {#1} } > { 0 }
      {
        \int_add:Nn \l@@_total_notes_int { \@@_note_count:n {#1} }
        \tl_put_right:Nx \l@@_summary_rows_tl
          {
            \exp_not:N \textcolor
              { \@@_note_color:n {#1} }
              {#1}
            \exp_not:N &
            \@@_note_count:n {#1}
            \exp_not:N \\
          }
      }
  }
\cs_new_protected:Npn \@@_print_notes:
  {
    \tl_clear:N \l@@_summary_rows_tl
    \int_zero:N \l@@_total_notes_int
    \seq_map_inline:Nn \g@@_note_types_seq
      { \@@_summary_row:n {##1} }
    \tl_if_empty:NF \l@@_summary_rows_tl
      {
        \par\nobreak
        \noindent\dotfill\par\medskip
        \nobreak
        \noindent\textbf{\Large Zebra~Notes}
        \par \medskip
        \begin{center}
          \begin{tabular}{lr}
            \hline
            \textbf{Type} & \textbf{Count} \\\hline
            \tl_use:N \l@@_summary_rows_tl
            \hline
            \textbf{Total} & \textbf{\int_use:N \l@@_total_notes_int} \\
            \hline
          \end{tabular}
        \end{center}
        \bool_if:NF \l@@_unnumbered_bool
          {
            \par \medskip
            \begingroup
              \small
              \noindent{\bfseries List~of~notes}\par
              \nobreak
              \bool_if:NTF \l@@_sort_none_bool
                { \@@_print_notes_inorder: }
                {
                  \seq_map_inline:Nn \g@@_note_types_seq
                    { \@@_print_note_group:n {##1} }
                }
            \endgroup
          }
      }
  }
%    \end{macrocode}
%
% \begin{macro}{\zebranewnote}
% All note types are created with \cs{zebranewnote}.
% \changes{v0.8.0}{2019/07/04}{Fix on \cs{global} for examples}
%    \begin{macrocode}
\NewDocumentCommand \zebranewnote { m m O{} }
  { \@@_new_note_type:nnn {#1} {#2} {#3} }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\zebrasetup}
% Applies configuration keys after loading using the
% \texttt{zebra-setup} key family.
%    \begin{macrocode}
\NewDocumentCommand \zebrasetup { m }
  { \keys_set:nn { zebra-setup } {#1} }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\todo}
% \begin{macro}{\note}
% \begin{macro}{\fixed}
% \begin{macro}{\comment}
% \begin{macro}{\placeholder}
% Built-in note types, defined with \cs{zebranewnote}.
%    \begin{macrocode}
\zebranewnote{todo}{purple}
\colorlet{zebra@fixed@color}{green!50!black}
\zebranewnote{fixed}{zebra@fixed@color}
\zebranewnote{comment}{blue}
\zebranewnote{note}{violet}
\zebranewnote{placeholder}{gray}
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
%\subsection{Print summary at end of the document}
%
% A summary table and a detailed note list are inserted automatically at the
% end of the document. Each note type with at least one instance is listed with
% its colour and count, followed by notes in document order or grouped by type.
%
%    \begin{macrocode}
%% At end of document: print the note summary and list.
%% Content signatures are written to .aux inline (at allocation time),
%% so no additional end-of-document aux writes are needed.
\hook_gput_code:nnn { enddocument } { zebra }
  {
    \bool_if:NT \l@@_draft_bool
      { \@@_print_notes: }
  }
\ExplSyntaxOff
%    \end{macrocode}
%    \begin{macrocode}
%</package>
%    \end{macrocode}
%
%\subsection{Compatibility shim}
%
% The old package name \pkg{zebra-goodies} is supported via a thin
% wrapper that loads \pkg{zebra} and prints a deprecation warning.
%    \begin{macrocode}
%<*compat>
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{zebra-goodies}
  [2026/04/29 v1.8.1 Deprecated: use zebra instead]
\PackageWarningNoLine{zebra-goodies}
  {Package 'zebra-goodies' is deprecated.\MessageBreak
   Use \string\usepackage{zebra} instead}
\RequirePackageWithOptions{zebra}
%</compat>
%    \end{macrocode}
%
%\subsection{Two-column demo}
%
% A standalone two-column document used to generate the demo figure
% included in the documentation. It is extracted automatically by
% docstrip and compiled during the build.
%    \begin{macrocode}
%<*demo-twocol>
\documentclass[twocolumn]{article}
\usepackage[paperwidth=21cm,paperheight=15cm,margin=1.1cm]{geometry}
\usepackage{zebra}
\zebrasetup{symbol/comment=$\clubsuit$}
\pagestyle{empty}
\begin{document}
\section{Demo name\comment{revise the name}}
This draft still needs work
\todo[alice]{\label{zebra:intro}revise the introduction}. The
opening paragraph should also explain the main goal more plainly.
Add one more citation here \note[bob]{support this claim}. A
brief roadmap sentence would also make the structure easier to
scan.

The issue raised in Todo~\ref{zebra:intro} still applies in
the conclusion. The table now looks fine
\fixed[carol]{alignment corrected}, but one figure is still
missing \placeholder[eve]{insert the overview figure}. A short
transition would also help the flow. The middle section should
probably end with a clearer summary sentence before the
discussion begins. That summary can stay compact, but it should
signal why the next section matters.

Please verify the totals \note[frank]{check the numbers} and
confirm the wording in the last paragraph
\comment[tom]{is this sentence too strong?}. A small typo has
already been fixed \fixed[heidi]{typo corrected}. The ending
should stay short. The final sentence should return to the main
claim rather than repeat background material. You can place
\todo[judy]{summarise the findings} anywhere once the narrative
is stable.

One more short paragraph is enough to show how \placeholder{wow,
so great!} the markers stay readable in a compact two-column layout.
The example is intentionally small, but it should still look like
a realistic revision pass.\comment{Bye}
\end{document}
%</demo-twocol>
%    \end{macrocode}
%
%\end{implementation}
%
%\PrintChanges
%\PrintIndex
