% ======================================================================
%  callout-itembox.sty  --  colored callout boxes with bullet/numbered marker
%  v1.0
%
%  Two content layouts:
%     \callout [<keys>] {<heading>}{<body>}   -- heading + body
%     \callout*[<keys>] {<body>}              -- body only, no heading
%
%  Built-in themes:  blue (default), red, teal, green, purple, orange, slate
%  Marker:           dot (default white circle)  |  number=<n>  |  number=<n>,vertical
%  Corners:          corners=sharp (default) | corners=rounded | arc=<len>
%  Shadow:           shadow=off (default) | shadow=on
%  Custom colours:   barcolor=, bgcolor=, headcolor=  (any xcolor expression)
%
%  Examples:
%     \callout{Primary Objective}{To establish a correlation ...}
%     \callout[theme=red]{Warning}{Do not proceed ...}
%     \callout[number=1]{Step one}{First do this ...}
%     \callout*[theme=red]{All parameters recorded post-op ...}
%     \callout*[theme=blue,number=2]{Body text only, numbered ...}
%
%  Copyright (C) 2026  <Shukla Mondal>
%  Released under the MIT License (see the LICENSE file).
%  Project home: https://github.com/<sm-cse>/callout-itembox
% ======================================================================
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{callout-itembox}[2026/06/07 v1.2 colored callout boxes]

\RequirePackage{xparse}
\RequirePackage{xcolor}
\RequirePackage{tikz}
\RequirePackage{tcolorbox}
\tcbuselibrary{skins,breakable}
\usetikzlibrary{calc}

% ---------------------------------------------------------------- colors
\definecolor{callout@blue@bar} {RGB}{1,32,96}
\definecolor{callout@blue@bg}  {RGB}{234,237,242}
\definecolor{callout@blue@head}{RGB}{1,32,96}
\definecolor{callout@red@bar}  {RGB}{192,0,0}
\definecolor{callout@red@bg}   {RGB}{252,238,232}
\definecolor{callout@red@head} {RGB}{192,0,0}
\colorlet{callout@teal@bar} {teal!70!black}
\colorlet{callout@teal@bg}  {teal!8}
\colorlet{callout@teal@head}{teal!60!black}
\definecolor{callout@green@bar} {HTML}{1B5E20}
\definecolor{callout@green@bg}  {HTML}{E8F0E9}
\definecolor{callout@green@head}{HTML}{1B5E20}
\colorlet{callout@purple@bar} {purple!75!black}
\colorlet{callout@purple@bg}  {purple!8}
\colorlet{callout@purple@head}{purple!70!black}
\colorlet{callout@orange@bar} {orange!85!black}
\colorlet{callout@orange@bg}  {orange!13}
\colorlet{callout@orange@head}{orange!70!black}
\definecolor{callout@slate@bar} {HTML}{37474F}
\definecolor{callout@slate@bg}  {HTML}{ECEFF1}
\definecolor{callout@slate@head}{HTML}{263238}

% colors actually used (set by theme / overridden by custom keys)
\colorlet{callout@bar} {callout@blue@bar}
\colorlet{callout@bg}  {callout@blue@bg}
\colorlet{callout@head}{callout@blue@head}

% ---------------------------------------------------------------- lengths
\newlength{\callout@barwd}   \setlength{\callout@barwd}{0.95cm} % width of colored bar
\newlength{\callout@gap}     \setlength{\callout@gap}{0.2cm}   % gap bar -> text
\newlength{\callout@rad}     \setlength{\callout@rad}{4.6pt}    % marker radius
\newlength{\callout@arc}     \setlength{\callout@arc}{0pt}      % corner radius (0 = sharp)

% ---------------------------------------------------------------- keys
\newif\ifcallout@number  \callout@numberfalse
\newif\ifcallout@shadow  \callout@shadowfalse
\newif\ifcallout@vert    \callout@vertfalse
\def\callout@num{1}

% apply a named theme by copying its three colors into the active ones
\newcommand{\callout@settheme}[1]{%
  \colorlet{callout@bar}{callout@#1@bar}%
  \colorlet{callout@bg}{callout@#1@bg}%
  \colorlet{callout@head}{callout@#1@head}}

\pgfkeys{
  /callout/.is family, /callout/.cd,
  theme/.is choice,
  theme/blue/.code   = {\callout@settheme{blue}},
  theme/red/.code    = {\callout@settheme{red}},
  theme/teal/.code   = {\callout@settheme{teal}},
  theme/green/.code  = {\callout@settheme{green}},
  theme/purple/.code = {\callout@settheme{purple}},
  theme/orange/.code = {\callout@settheme{orange}},
  theme/slate/.code  = {\callout@settheme{slate}},
  number/.code     = {\callout@numbertrue\def\callout@num{#1}},
  % vertical (rotated) number marker
  vertical/.is choice,
  vertical/on/.code    = {\callout@verttrue},
  vertical/off/.code   = {\callout@vertfalse},
  vertical/true/.code  = {\callout@verttrue},
  vertical/false/.code = {\callout@vertfalse},
  vertical/.default    = on,
  % corner style
  corners/.is choice,
  corners/sharp/.code   = {\setlength{\callout@arc}{0pt}},
  corners/rounded/.code = {\setlength{\callout@arc}{4pt}},
  arc/.code        = {\setlength{\callout@arc}{#1}},
  % drop shadow
  shadow/.is choice,
  shadow/on/.code    = {\callout@shadowtrue},
  shadow/off/.code   = {\callout@shadowfalse},
  shadow/true/.code  = {\callout@shadowtrue},
  shadow/false/.code = {\callout@shadowfalse},
  shadow/.default    = on,
  % custom colour overrides (optional)
  barcolor/.code   = {\colorlet{callout@bar}{#1}},
  bgcolor/.code    = {\colorlet{callout@bg}{#1}},
  headcolor/.code  = {\colorlet{callout@head}{#1}},
  % geometry overrides (optional)
  barwidth/.code   = {\setlength{\callout@barwd}{#1}},
  gap/.code        = {\setlength{\callout@gap}{#1}},
}

% ------------------------------------------------------------ internal box
% #1 = key/value options   #2 = heading (may be empty)   #3 = body
\newcommand{\callout@build}[3]{%
  % reset to defaults each call, then apply user keys
  \callout@numberfalse \def\callout@num{1}\setlength{\callout@arc}{0pt}\callout@shadowfalse\callout@vertfalse%
  \pgfkeys{/callout/.cd, theme=blue}%
  \pgfkeys{/callout/.cd, #1}%
  % decide shadow as a style (kept out of the option list so the comma
  % inside the shadow spec does not break key parsing)
  \ifcallout@shadow
    \tcbset{callout@shadowstyle/.style={drop fuzzy shadow={black!45}}}%
  \else
    \tcbset{callout@shadowstyle/.style={}}%
  \fi
  \begin{tcolorbox}[
      enhanced, breakable,
      arc=\callout@arc, outer arc=\callout@arc, boxrule=0pt, frame hidden,
      colback=callout@bg,
      left=\dimexpr\callout@barwd+\callout@gap\relax,
      right=5pt, top=4pt, bottom=4pt,
      before skip=4pt, after skip=4pt,
      callout@shadowstyle,
      overlay={%
        % clip to the (possibly rounded) box outline so the bar follows the corners
        \begin{scope}
          \clip[rounded corners=\callout@arc]
            (frame.south west) rectangle (frame.north east);
          \fill[callout@bar]
            (frame.north west) rectangle
            ([xshift=\callout@barwd]frame.south west);
        \end{scope}
        % marker, vertically centered on the bar
        \ifcallout@number
          \ifcallout@vert
            \node[anchor=center, white, font=\bfseries\large, rotate=90]
              at ([xshift=0.5\callout@barwd]frame.west) {\callout@num};
          \else
            \node[anchor=center, white, font=\bfseries\large]
              at ([xshift=0.5\callout@barwd]frame.west) {\callout@num};
          \fi
        \else
          \fill[white]
            ([xshift=0.5\callout@barwd]frame.west) circle[radius=\callout@rad];
        \fi
      },
    ]
    \callout@maybehead{#2}#3%
  \end{tcolorbox}%
}

% print heading only when non-empty
\newcommand{\callout@maybehead}[1]{%
  \begingroup\edef\callout@tmp{#1}%
  \ifx\callout@tmp\empty\else
    {\normalfont\bfseries\large\color{callout@head}#1\par\nobreak\vspace{2pt}}%
  \fi\endgroup}

% ------------------------------------------------------------ public macro
% star  -> body only          \callout*[keys]{body}
% plain -> heading + body      \callout[keys]{heading}{body}
\NewDocumentCommand{\callout}{ s O{} m G{\NoValue} }{%
  \IfBooleanTF{#1}
    {\callout@build{#2}{}{#3}}%            starred: #3 = body
    {\IfNoValueTF{#4}
       {\callout@build{#2}{}{#3}}%         only one arg given -> treat as body
       {\callout@build{#2}{#3}{#4}}}%      heading + body
}

\endinput
