%% seasonalletters.sty -- configurable decorative stationery
%% Copyright (c) 2026 aqsa <aqsa.umar3505@gmail.com>
%% Distributed under the MIT License; see LICENSE.
\NeedsTeXFormat{LaTeX2e}[2020/10/01]
\ProvidesPackage{seasonalletters}[2026/11/05 v0.1.0 Seasonal letter stationery]
\RequirePackage{tikz}
\RequirePackage{graphicx}
\RequirePackage{etoolbox}
\usetikzlibrary{calc,shapes.geometric}
\newif\ifsl@enabled \sl@enabledtrue
\newif\ifsl@print \sl@printfalse
\newif\ifsl@document \sl@documentfalse
\newif\ifsl@full
\newif\ifsl@debug \sl@debugfalse
\newcount\sl@page
\def\sl@season{spring}\def\sl@weekday{monday}\def\sl@day{1}
\def\sl@paper{plain}\def\sl@continuation{light}
\def\sl@border{double}\def\sl@label{}\def\sl@image{}
\def\sl@imageopacity{0.12}\def\sl@spacing{8mm}
\def\sl@left{30mm}\def\sl@right{30mm}
\def\sl@top{45mm}\def\sl@bottom{35mm}
\def\sl@cornerwidth{18mm}\def\sl@cornerimage{}
\def\sl@customart{}
\def\sl@palette#1#2#3#4{%
  \definecolor{slPaper}{HTML}{#1}%
  \definecolor{slAccent}{HTML}{#2}%
  \definecolor{slMuted}{HTML}{#3}%
  \definecolor{slInk}{HTML}{#4}}
\sl@palette{FAF5E9}{7B5260}{73816A}{302C29}
\pgfkeys{/seasonalletters/.is family,/seasonalletters,
  season/.is choice,
  season/spring/.code={\def\sl@season{spring}\sl@palette{FAF5E9}{7B5260}{73816A}{302C29}},
  season/summer/.code={\def\sl@season{summer}\sl@palette{FCF5E3}{9B702F}{7C8760}{302C26}},
  season/autumn/.code={\def\sl@season{autumn}\sl@palette{F4EBDD}{946044}{7B7358}{352D27}},
  season/winter/.code={\def\sl@season{winter}\sl@palette{F4F3EE}{435D72}{8E907D}{29333B}},
  weekday/.is choice,
  weekday/monday/.code={\def\sl@weekday{monday}\def\sl@day{1}},
  weekday/tuesday/.code={\def\sl@weekday{tuesday}\def\sl@day{2}},
  weekday/wednesday/.code={\def\sl@weekday{wednesday}\def\sl@day{3}},
  weekday/thursday/.code={\def\sl@weekday{thursday}\def\sl@day{4}},
  weekday/friday/.code={\def\sl@weekday{friday}\def\sl@day{5}},
  weekday/saturday/.code={\def\sl@weekday{saturday}\def\sl@day{6}},
  weekday/sunday/.code={\def\sl@weekday{sunday}\def\sl@day{7}},
  paper/.is choice,
  paper/plain/.code={\def\sl@paper{plain}},
  paper/ruled/.code={\def\sl@paper{ruled}},
  paper/grid/.code={\def\sl@paper{grid}},
  continuation/.is choice,
  continuation/light/.code={\def\sl@continuation{light}},
  continuation/full/.code={\def\sl@continuation{full}},
  continuation/none/.code={\def\sl@continuation{none}},
  border/.is choice,
  border/double/.code={\def\sl@border{double}},
  border/single/.code={\def\sl@border{single}},
  border/none/.code={\def\sl@border{none}},
  output/.is choice,
  output/screen/.code={\sl@printfalse},
  output/print/.code={\sl@printtrue},
  enabled/.is choice,
  enabled/true/.code={\sl@enabledtrue},
  enabled/false/.code={\sl@enabledfalse},
  debug/.is choice,
  debug/true/.code={\sl@debugtrue},
  debug/false/.code={\sl@debugfalse},
  label/.store in=\sl@label,
  spacing/.store in=\sl@spacing,
  left/.store in=\sl@left,right/.store in=\sl@right,
  top/.store in=\sl@top,bottom/.store in=\sl@bottom,
  background-image/.store in=\sl@image,
  background-opacity/.store in=\sl@imageopacity,
  corner-image/.store in=\sl@cornerimage,
  corner-width/.store in=\sl@cornerwidth,
  accent/.code={\colorlet{slAccent}{#1}},
  paper-color/.code={\colorlet{slPaper}{#1}},
  ink/.code={\colorlet{slInk}{#1}},
  muted/.code={\colorlet{slMuted}{#1}},
  theme/.code={\ifcsdef{sl@theme@#1}{\csuse{sl@theme@#1}}{%
    \PackageError{seasonalletters}{Unknown theme '#1'}{Define it with \string\SeasonalDeclareTheme.}}},
  .unknown/.code={\PackageError{seasonalletters}{Unknown option '\pgfkeyscurrentname'}{See the package manual for supported keys.}}
}
\def\sl@option#1{\pgfkeys{/seasonalletters,#1}}
\DeclareOption*{\expandafter\sl@option\expandafter{\CurrentOption}}
\ProcessOptions\relax
\AtBeginDocument{\sl@documenttrue}
\newcommand\SeasonalSetup[1]{%
  \ifsl@document\clearpage\fi
  \pgfkeys{/seasonalletters,#1}}
\newcommand\SeasonalDeclareTheme[2]{\csdef{sl@theme@#1}{\pgfkeys{/seasonalletters,#2}}}
\newcommand\SeasonalSetArtwork[1]{%
  \ifsl@document\clearpage\fi\def\sl@customart{#1}}
\newcommand\SeasonalReset{\ifsl@document\clearpage\fi\global\sl@page=0\relax}
\newcommand\SeasonalHeading[1]{%
  \par\noindent{\color{slAccent}\Large\scshape #1}\par\vspace{1.3em}}
\newcommand\SeasonalOpening[1]{\par\noindent #1\par\vspace{0.6em}}
\newcommand\SeasonalClosing[2]{\par\vspace{1.2em}\noindent #1\par\vspace{1.5em}\noindent #2\par}
\newenvironment{SeasonalLetter}[1][]{%
  \clearpage\begingroup
  \pgfkeys{/seasonalletters,enabled=true,#1}%
  \global\sl@page=0\relax\color{slInk}%
}{\par\clearpage\endgroup}
% Original vector motifs. Local coordinates are millimetres.
\def\sl@sprig{%
  \draw[slMuted,line width=.45pt] (0,0) .. controls (3,9) and (8,20) .. (5,33);
  \foreach \y/\r in {5/25,12/-25,19/25,26/-25}{%
    \begin{scope}[shift={(4,\y)},rotate=\r]
      \draw[slMuted,fill=slMuted!18,line width=.3pt]
        (0,0) .. controls (-6,1) and (-7,5) .. (-6,7)
        .. controls (-1,6) and (0,3) .. (0,0);
      \draw[slMuted,line width=.2pt] (0,0)--(-5,6);
    \end{scope}}
  \foreach \x/\y in {5/33,2/21,5/10}{%
    \begin{scope}[shift={(\x,\y)}]
      \foreach \a in {0,72,144,216,288}{%
        \draw[slAccent!85,fill=slAccent!13,line width=.25pt,rotate=\a]
          (0,0) .. controls (-2,1) and (-2,3) .. (0,3.3)
          .. controls (2,3) and (2,1) .. (0,0);}
      \fill[slAccent] (0,0) circle (.55);
    \end{scope}}}
\def\sl@summer{%
  \draw[slMuted,line width=.4pt] (0,0) .. controls (7,11) and (1,23) .. (6,32);
  \foreach \x/\y in {3/8,3/18,6/30}{%
    \begin{scope}[shift={(\x,\y)}]
      \foreach \a in {0,45,...,315}
        \draw[slAccent,fill=slPaper,line width=.25pt,rotate=\a]
          (0,0) ellipse (.6 and 2.5);
      \fill[slAccent!80] (0,0) circle (1);
    \end{scope}}
  \foreach \y in {4,13,22}
    \draw[slMuted,line width=.35pt] (3,\y) .. controls (8,\y+2) .. (9,\y+6)
      .. controls (4,\y+5) .. (3,\y);}
\def\sl@autumn{%
  \draw[slMuted,line width=.45pt] (0,0) .. controls (5,13) and (3,21) .. (6,34);
  \foreach \y/\r in {5/15,15/-35,25/25}{%
    \begin{scope}[shift={(4,\y)},rotate=\r]
      \draw[slAccent,fill=slAccent!12,line width=.3pt]
        (0,0)--(-4,2)--(-3,4)--(-6,5)--(-4,7)--(-5,9)--(-2,9)--(0,12)
        --(2,9)--(5,9)--(4,7)--(6,5)--(3,4)--(4,2)--cycle;
      \draw[slMuted,line width=.25pt] (0,-2)--(0,10);
    \end{scope}}}
\def\sl@winter{%
  \draw[slAccent,line width=.5pt] (0,0) .. controls (4,12) and (3,24) .. (7,35);
  \foreach \y in {6,14,22,29}{%
    \draw[slAccent,line width=.3pt] (4,\y)--(-3,\y+7) (-1,\y+5)--(-1,\y+8)
      (4,\y)--(11,\y+5) (8,\y+3)--(11,\y+2);
    \fill[slMuted] (11,\y+5) circle (.45);}
  \foreach \x/\y in {-4/19,11/14,0/32}
    \node[star,star points=4,star point ratio=3,fill=slMuted,
      inner sep=.7pt] at (\x,\y) {};}
\def\sl@motif{\csuse{sl@\sl@season}}
\let\sl@spring\sl@sprig
\def\sl@emblem{%
  \ifcase\sl@day\relax\or
    \draw[slAccent,line width=.5pt] (0,0) circle (2.7);
    \fill[slPaper] (1.5,1) circle (2.5);
  \or
    \draw[slAccent,line width=.45pt] (0,-3)--(0,3) (-3,0)--(3,0);
    \draw[slAccent,fill=slAccent!12] (0,3)--(2,0)--(0,-3)--(-2,0)--cycle;
  \or
    \draw[slAccent,line width=.45pt] (-3,-2.5) rectangle (3,2.5);
    \draw[slAccent,line width=.3pt] (-1,-2.5)--(-1,2.5) (1,-2.5)--(1,2.5)
      (-3,-.8)--(3,-.8) (-3,.8)--(3,.8);
  \or
    \draw[slAccent,line width=.4pt] (0,-3)--(0,3);
    \foreach \y in {-2,0,2}
      \draw[slAccent] (0,\y) .. controls (-3,\y) .. (-2,\y+2)
        (0,\y) .. controls (3,\y) .. (2,\y+2);
  \or
    \node[star,star points=5,star point ratio=2.5,draw=slAccent,
      fill=slAccent!15,minimum size=6mm,inner sep=0pt] at (0,0) {};
  \or
    \draw[slAccent,line width=.4pt] (0,0) circle (2.5);
    \draw[slAccent,fill=slAccent!15] (0,4)--(1,0)--(0,-4)--(-1,0)--cycle;
    \draw[slAccent] (-4,0)--(4,0);
  \or
    \draw[slAccent,line width=.45pt] (0,0) circle (1.7);
    \foreach \a in {0,30,...,330}
      \draw[slAccent,line width=.4pt] (\a:2.3)--(\a:3.5);
  \fi}
\def\sl@validate{%
  \ifdim\sl@spacing>0pt\else
    \PackageError{seasonalletters}{Spacing must be positive}{Use, for example, spacing=8mm.}%
    \def\sl@spacing{8mm}%
  \fi
  \ifdim\dimexpr\paperwidth-\sl@left-\sl@right\relax>0pt\else
    \PackageError{seasonalletters}{Writing rectangle has no width}{Reduce left and right insets.}%
  \fi
  \ifdim\dimexpr\paperheight-\sl@top-\sl@bottom\relax>0pt\else
    \PackageError{seasonalletters}{Writing rectangle has no height}{Reduce top and bottom insets.}%
  \fi}
\def\sl@render{%
  \ifsl@enabled
    \sl@validate\global\advance\sl@page by1
    \begingroup
    \sl@fullfalse
    \ifnum\sl@page=1 \sl@fulltrue\fi
    \ifdefstring{\sl@continuation}{full}{\sl@fulltrue}{}%
    \ifsl@print
      \colorlet{slPaper}{white}\colorlet{slAccent}{black!65}\colorlet{slMuted}{black!45}%
    \fi
    \begin{tikzpicture}[remember picture,overlay,x=1mm,y=1mm]
      \fill[slPaper] (current page.south west) rectangle (current page.north east);
      \ifsl@print\else
        \ifdefempty{\sl@image}{}{%
          \node[inner sep=0pt,opacity=\sl@imageopacity] at (current page.center)
            {\includegraphics[width=\paperwidth,height=\paperheight]{\sl@image}};}
      \fi
      \coordinate (slNW) at ([xshift=\sl@left,yshift=-\sl@top]current page.north west);
      \coordinate (slSE) at ([xshift=-\sl@right,yshift=\sl@bottom]current page.south east);
      % Rules are clipped to the declared writing rectangle.
      \ifsl@print\else
        \begin{scope}
          \clip (slNW) rectangle (slSE);
          \ifdefstring{\sl@paper}{plain}{}{%
            \pgfmathtruncatemacro{\sl@rows}{(\paperheight-\sl@top-\sl@bottom)/\sl@spacing}
            \foreach \r in {1,...,\sl@rows}{%
              \draw[slMuted!17,line width=.2pt]
                ([yshift=-\r*\sl@spacing]slNW)--([yshift=-\r*\sl@spacing]slNW -| slSE);}
            %
            \ifdefstring{\sl@paper}{grid}{%
              \pgfmathtruncatemacro{\sl@cols}{(\paperwidth-\sl@left-\sl@right)/\sl@spacing}
              \foreach \c in {1,...,\sl@cols}{%
                \draw[slMuted!17,line width=.2pt]
                  ([xshift=\c*\sl@spacing]slNW)--([xshift=\c*\sl@spacing]slNW |- slSE);}
              % 
            }{}}
        \end{scope}
      \fi
      \def\sl@drawdecor{1}%
      \ifnum\sl@page>1
        \ifdefstring{\sl@continuation}{none}{\def\sl@drawdecor{0}}{}%
      \fi
      \ifnum\sl@drawdecor=1
        \ifdefstring{\sl@border}{none}{}{%
          \draw[slAccent!65,line width=.35pt]
            ([xshift=11mm,yshift=11mm]current page.south west)
            rectangle ([xshift=-11mm,yshift=-11mm]current page.north east);
          \ifdefstring{\sl@border}{double}{%
            \draw[slAccent!35,line width=.2pt]
              ([xshift=13mm,yshift=13mm]current page.south west)
              rectangle ([xshift=-13mm,yshift=-13mm]current page.north east);
          }{}}
        \ifsl@full
          \begin{scope}[shift={([xshift=18mm,yshift=19mm]current page.south west)}]
            \sl@motif
          \end{scope}
          \begin{scope}[shift={([xshift=-18mm,yshift=-19mm]current page.north east)},rotate=180]
            \sl@motif
          \end{scope}
          \begin{scope}[shift={([yshift=-24mm]current page.north)}]
            \draw[slAccent!50,line width=.3pt] (-25,0)--(-8,0) (8,0)--(25,0);
            \sl@emblem
          \end{scope}
          \ifdefempty{\sl@label}{}{%
            \node[text=slAccent,font=\normalfont\small\scshape,inner sep=0pt]
              at ([yshift=-35mm]current page.north) {\sl@label};}
          \ifdefempty{\sl@cornerimage}{}{%
            \node[anchor=south east,inner sep=0pt]
              at ([xshift=-16mm,yshift=16mm]current page.south east)
              {\includegraphics[width=\sl@cornerwidth]{\sl@cornerimage}};}
          \sl@customart
        \else
          \begin{scope}[shift={([yshift=-22mm]current page.north)},scale=.7]
            \sl@emblem
          \end{scope}
        \fi
        \node[text=slAccent!70,font=\normalfont\scriptsize,inner sep=0pt]
          at ([yshift=19mm]current page.south) {\the\sl@page};
      \fi
      \ifsl@debug
        \draw[red,dashed,line width=.4pt] (slNW) rectangle (slSE);
      \fi
    \end{tikzpicture}%
    \endgroup
  \fi}
\AddToHook{shipout/background}[seasonalletters]{\sl@render}
\endinput
