# Changelog

All notable changes to `linguexx`. Versions refer to the `\ProvidesPackage`
version string.

## 1.4
- New: movement arrows. `\mvto{a}{What} did John buy \mvfrom{a}{\_\_}?`
  draws an arrow from the base position (`\mvfrom`) to the landing site
  (`\mvto`), below the line, or above the object line in a gloss. Several
  movements in one example are stacked out of each other's way, and the
  line gets the room they need; a word can be both ends of a chain, whose
  links stay on one level. Options
  `above`, `below`, `level=<n>` and `spoken=<text>`; the arrow's look is the
  TikZ style `lx move`. Two runs, like `\ref`, and the log asks for the
  second; in `beamer` every slide has its own arrows, so they stay with
  their words when a `\pause` or `\only` moves the text. In a tagged PDF
  the arrow is an artifact and the base position is read as "__ (moved)";
  `\SetMoveSpoken` changes the note. Long names `\lxMoveFrom`,
  `\lxMoveTo`, for a document where the short ones are taken.
- New: inline verbatim in a dot-syntax example. `\verb`, `\verb*`,
  fancyvrb's `\Verb` and listings' `\lstinline` work in `\ex.`, in
  sub-examples and as words of a gloss, with their own stars, options and
  delimiters; until now they stopped the document with "Missing $
  inserted", and the manual called that inherent. An example inside
  another command's argument (`\footnote{...}`) still cannot hold
  verbatim text, and says so in an error; other verbatim commands and
  environments still need the `exe` syntax.
- Fixed: with `cleveref`, a `\label` in the prose after an example
  (`\section{..} \ex. .. Prose.\label{s}`) was referred to by `\cref` as
  the example, "(1)" for "section 1", while `\ref` printed it right.
- Fixed: `\label` on a custom-labelled example (`\ex.[(7)]\label{x}`)
  recorded nothing, so `\ref{x}` printed an empty string, with no
  warning. It now prints the label as a number would be printed, "(7)"
  (or "7" under `\ExBareRefs` and `[langsci]`; a label without the example
  brackets as it stands), and the example has a link target of its own.
  `\cref` lists several of them without joining them into a range, and
  puts them after the numbered ones in a list that mixes the two. The
  same holds for `[langsci]`'s `\exi` and `\exr`, where a `\label` used to
  record the previous item's label. `\pref` is now robust, like `\ref`.
- Fixed: a sub-example under a custom label (`\ex.[(5)] \a. ...`) referred
  to itself with the example counter's value instead of its label: `\ref`
  to letter a printed "(0a)" before any numbered example, and the number
  of the last numbered example after one. Every custom-labelled example
  also claimed the same hyperref anchors, so each such `\ref` led to the
  first of them. The reference now prints the label with the letter,
  "(5a)" (brackets taken off and put back as for a number), and each
  example has anchors of its own.
- Changed: hyperref anchors are built from a serial that nothing resets,
  not from the printed number, so two examples never share a destination
  -- after `\setcounter{ExNo}{0}`, or `[legacy]`'s per-chapter reset, a
  `\label` on the second example led to the first. A document without
  resets or custom labels keeps exactly the anchors it had. Custom-labelled
  examples use the stem `lxcex.`. The `.aux` records of the relative
  references change form (`\lx@relref@num`): the first run after the
  update asks for a rerun.
- Fixed: an empty gloss cell written `{}` was dropped, so every gloss after
  it moved one column to the left and sat under the wrong word, with
  nothing in the log. `{}` is now an empty cell, in any column; `{ }`
  already was. Documents without an empty `{}` are unchanged, page for
  page.
- Changed: the package options are declared with the LaTeX kernel's key
  interface, which needs LaTeX 2022-06-01 or later (was 2020-10-01). The
  options and their names are the same, and still take no value: where
  1.3.2 ignored `[legacy=true]` with a warning, it is now an error, "The
  option 'legacy' takes no value" -- write `[legacy]`. An unknown option
  still only warns.
- Manual: "Notes and limitations" says that in a tagged document a
  sub-example list broken across a page needs LaTeX 2026-06-01: older
  kernels can break the structure tree there (the kernel's bug, not
  linguexx's), with a workaround for them.
- The CTAN package ships `CHANGELOG.md` beside the manual, in place of the
  release history the `.sty` header used to repeat.

## 1.3.2
- Fixed: in a tagged PDF, the spoken form of an `\altn` or `\altg` stack
  was attached to nothing. Under LuaLaTeX every stack's `/Alt` sat on a
  structure element with no content, and the alternatives stayed
  children of the surrounding paragraph; under every engine, so did an
  `\altn` alternative that contained a `\lpzg` gloss. The page was
  correct and veraPDF passed the file, so nothing reported it. A
  screen reader now finds the alternatives inside the element whose
  `/Alt` describes them. Tagged documents that use either command should
  be recompiled.
- Manual: "Notes and limitations" says that an `\altn` stack nested
  inside another prints correctly but is read out wrongly.

## 1.3.1
- Fixed: under `[phantomalign]`, a gloss word that repeats its object
  word's delimiters -- `(è questa)` glossed `(est celle-ci)` -- was set one
  delimiter-width too far right. The pad is now the difference between the
  two words' leading marks, each in its own tier's font.
- Fixed: the CTAN archive ships the manual's source, `linguexx-doc.tex`.
- Manual: the table of layout lengths (§8) breaks across pages instead of
  running off the bottom of one.
- Manual: `\GlossPhantomChars` explains how to declare a mark that
  contains math.

## 1.3
- Fixed: `\renewcommand{\ExLBr}{[}` did nothing. `linguex` has had
  `\ExLBr`/`\ExRBr` (and `\FnExLBr`/`\FnExRBr` for the footnote series)
  since its version 4.0 and documents them as the way to get `[1]` instead
  of `(1)`; this package printed its parentheses from `\theExLBr`, which is
  spelled differently, so a document that asked for square brackets was
  accepted and ignored. That is the one kind of incompatibility a drop-in
  replacement cannot have: the document still compiles and only the page is
  wrong. The characters now live in the four `linguex` names and the
  `\the...` layer reads them, so both spellings work and mean different
  things -- `\ExLBr` is the character, `\theExLBr` the character plus the
  parenthesis suppression `\ExBareRefs` asks for. The footnote pair stays
  separate, as upstream: moving `\ExLBr` alone leaves a footnote example
  at `(i)`.
- Fixed: loading a package this one replaces was silent. `linguex`,
  `expex`, `gb4e` and `langsci-gb4e` all define `\ex`, all with `\def`,
  and so does this one -- so whichever file was read second simply won and
  the document compiled into a mixture that kept numbering. A document
  being ported is exactly where that happens, because the old
  `\usepackage` line is the last thing anyone deletes. All four are now
  refused by name, in both load orders. `expex` read as plain `\TeX` with
  `\input` leaves nothing to detect, so that one is a warning saying `\ex`
  is no longer the package's. `cgloss4e` is deliberately not on the list:
  whether a document may keep a foreign glossing package is the question
  `doc/DEFERRED-DECISIONS.md` leaves open under `[nocgloss]`, and an error
  would answer it by accident.
- New: `\SetAltSpoken{word}` sets the connector a screen reader hears between
  the alternatives of a stack — `\altn{aa}{bb}` was announced "aa or bb" in
  every document, whatever its language, and there was no way to say "aa ou
  bb". It applies to `\altg` as well, since both build their spoken form
  through the same code.
- The punctuation goes with it. English puts a comma before the final
  connector ("aa, bb, or cc") and most other languages do not, so
  `\SetAltSpoken{ou}` drops it: "aa, bb ou cc". The starred form keeps it,
  and the optional argument replaces the punctuation itself, so the package's
  own default is exactly `\SetAltSpoken*{or}[,]`. A hook that cannot express
  the default it replaces would hide one of the two behaviours from the
  author.
- Write the word bare: spacing is the package's business, so
  `\SetAltSpoken{ou}` and `\SetAltSpoken{ ou }` are one setting. An empty
  connector joins with punctuation alone.
- The setting is **local**, like the `xlist` variants and unlike
  `\SetLeipzig`: a French example can be fenced in a group without the rest
  of the document following it. A preamble setting is made at top level and
  so still applies throughout.
- Fixed: a `\label` at the head of an example stopped the judgment scan, so
  `\ex. \label{ex:2}*?La sola invasione ...` set the `*?` in the text block
  instead of hanging it in the gutter and indented the example by its width.
  Since a label is typed at the head far more often than after the first word,
  the examples that lost the alignment were precisely the cross-referenced
  ones. The same held for `\sublabel` on a sub-example (`\ag.`, `\a.`) and
  for the glossed shorthands, where the object tier moved out from under its
  own number.
- `\label` and `\sublabel` are now skipped past and *replayed*: taken out of
  the input so the scan reaches the marks behind them, and put back
  immediately after the `\item`, which is where writing them in the body
  always put them. `\ref`, `\prefrange` and the PDF anchor are therefore
  unchanged — including the depth `\sublabel` records, which is what
  `\prefrange` prints as the closing half of a range. An optional argument
  (cleveref's `\label[eq]{...}`) is carried across with the rest, and
  recognition is by meaning, so the hyperref, cleveref and babel
  redefinitions of `\label` are all the same `\label` to it.
- Test suite: cases now compile in parallel, which takes a full run from 383s
  to about 85s on a six-core machine with two threads a core — 69s at twelve
  jobs, but see the next entry for why that is not the default (`-j1` for the
  old behaviour). Six cores put the floor at about 64s, so that is most of
  what is available. Cases were already independent — each builds its own
  temporary directory and compiles there — so serial execution was only
  buying ordered output, and that is now recovered by printing along a plan
  built before the work starts. The report is byte-identical to a serial run.
- The default is six jobs capped at the CPUs present, not `os.cpu_count()` as
  it first was, and each case now compiles with a `TMPDIR` of its own. That
  count is of logical threads, and running twelve of them exposed a race that
  is nobody's fault here: xdvipdfmx creates its scratch file in `$TMPDIR` with
  `mkstemp`, then reopens it **by name** and unlinks it by name, so two
  concurrent runs that draw the same name remove each other's file. The loser
  exits 1 with its log truncated mid-preamble — no TeX error, no signal, no
  coredump, and the same directory rebuilding cleanly a second later, which is
  indistinguishable from a real regression except that it never reproduces.
  Measured at one failure in 600 runs of a single case, which across the
  xelatex half of a full run is roughly one poisoned run in five. A private
  `TMPDIR` takes away the shared namespace the race needs: no
  `/tmp/dvipdfmx.*` file appears during a full xelatex run any more (31 did in
  60 unisolated compiles), and the suite is green at `-j24`. Six survives the
  fix because the oversubscription is real on its own — a case is an engine
  followed by pdftotext, pdfinfo, qpdf, a render, and for the PDF/UA case a
  JVM — and the cap keeps a two-core CI runner from re-creating it.
- This closes one of the three English-bound spoken forms named in §9.4 of
  the manual; the judgment phrases and the Leipzig expansions were already
  overridable. Which sets a non-English document *should* use remains an open
  question — see `doc/DEFERRED-DECISIONS.md`.
- New: `\GlossTransSide` sets `\glt`'s free translation in a column **beside**
  the interlinear grid instead of underneath it; `\GlossTransBelow` restores
  the default. It saves height when the translation is short beside a gloss of
  some height — the short column then costs nothing, hiding inside the tall
  one — and *costs* height when the translation is the longer of the two,
  because narrowing the measure makes both columns taller. Measured: a
  one-line translation goes from 71.1pt to 57.6pt, a three-line one from
  84.7pt to 94.1pt. The manual says so rather than leaving it to be found.
  `\GlossTransRatio` (`.6`) divides the measure, `\GlossTransSep` (`2em`)
  separates the columns — both `expex`'s own values, so the same example set
  with either package comes out in the same proportions. Manual §7.4; `tests/glt-side.tex` for the geometry
  and `tests/ua.tex` for the tagging.

  **A declaration, and not something on `\glt`.** The decision has to be made
  while the grid is still being built: by the time `\glt` is reached the grid
  is a finished paragraph contributed to the enclosing list, and there is
  nothing left to set beside. `\glt` is therefore unchanged — it still takes
  no argument — and a document that does not ask for the side position is
  unaffected. It respects grouping, like `\ExAnnotFit` and `\ExRaggedRight`.

  **Two refusals, both package errors.** Top-level examples only: below that
  the measure is already reduced twice, so both columns come out narrow and
  the grid starts wrapping, and a split taken from the indented measure would
  put every sibling's translation at a different place. And no `\exannot` on
  the same gloss: its column is measured from `\columnwidth`, which says
  nothing once the grid has been narrowed to half of it. If there is simply no
  room, the translation goes underneath after all and the log says why
  (`\GlossTransMinWidth`, default `6em`).

  Four bugs during implementation, every one of them a box's reference point
  or a question of what mode TeX was in, and every one of them silent. The
  grid is a *paragraph* of column boxes, so opening the side box left TeX in
  internal vertical mode and the first column became a line of its own — at
  any width, so a wider column hid it rather than fixing it. `\glt`'s
  `\nobreak` is a penalty, and a `\vtop` whose first item is not a box has
  height zero, so opening the translation box before it hung the whole
  translation half a line low. A `\vbox` would have aligned the two columns
  on their last baselines rather than their first. And the placement has to
  suspend the ambient marked content, or the two boxes nest theirs inside the
  paragraph that puts them down — 19 records logged by veraPDF while every
  one of its profiles still called the file compliant. `tests/glt-side.tex`
  and `tests/ua.tex` have a mutation for each.
- New: `\exannot[⟨spoken⟩]{⟨text⟩}` — a structural label (`[CP]`, `[TP]`, a
  language name, a reading) set in a **column** beside the examples. `\exsource`
  is the flush-right member of the family and is for a source, which belongs at
  the margin; this one is for a label that belongs next to the example it
  labels, and next to several examples it has to line up or the eye cannot use
  it. `\ExAnnotColumn` says where the column is, measured from the left edge of
  the text block (default `.75\columnwidth`); `\ExAnnotSep` is the least gap
  (default `1em`); `\ExAnnotFont` the font. Manual §7.4; `tests/exannot.tex`
  measures the geometry and `tests/exannot-ua.tex` the structure tree.

  **Why not `\jambox`.** `[langsci]` already carries `\jambox`, which measures
  from the right margin, and its gutter can be widened to bring the column
  nearer the examples. It does not survive that: it sets its box at its natural
  width behind glue that shrinks to nothing, so an example long enough to reach
  the column pushes the annotation aside instead of wrapping — silently, with no
  overfull warning. Four sub-examples of increasing length at one gutter came
  out aligned to 0.01pt at 3cm and at 7cm, and at 212.6 / 212.6 / 234.1 /
  288.9pt at 11cm. A column set *near* the examples is exactly the regime where
  it stops holding, which is why this is a new command and not a wrapper.
  `\jambox` is untouched: under `[langsci]` it is what `langsci-gb4e` has.

  `\exannot` instead carries the annotation in a box of **fixed width** ending
  at the right margin, so nothing on the line can move its left edge, and the
  column holds across nesting levels (measured from `\columnwidth`, not
  `\linewidth`, which has already lost the indentation — a `\linewidth` column
  drifts one indent step per level and looks deliberate on the page). An
  example that would come within `\ExAnnotSep` of the column takes the break
  instead: its annotation drops to the next line, in the same column.

  **`\ExAnnotFit`** measures the column instead of taking it. Each annotated
  example records where its text ended, and the column of an example — one
  `\ex.` and everything under it — is put `\ExAnnotSep` past the longest of
  them. The unit is the example, not the document, so a block of short
  examples gets its own column; footnote examples are their own examples in
  this sense too. The widest *annotation* of a block counts as well and the
  column is never narrower than it, which is what keeps a long label in line
  with its neighbours instead of letting it step out to the left on its own.
  When the two pull against each other, the column wins and the annotation of
  the example it had to move past goes onto the next line.

  Off by default, and off means off: nothing is recorded and no round trip
  happens unless it is asked for. When it is, the two measurements per
  annotation go through the `.aux` (`\pdfsavepos` on pdfTeX/XeTeX,
  `\savepos` on LuaTeX — no new package), so a fresh document's first run
  falls back to `\ExAnnotColumn` and says "Annotation columns are not
  settled. Rerun". The second is right and silent: the warning fires on an
  unmeasured run, and afterwards only when a width actually moves, so an
  ordinary two-run build is not told to run a third time for nothing. What
  is recorded is the *difference* between the two positions, never a
  position on its own — so it is a width, carrying no page origin, and it
  stays comparable across a page break, a `twoside` document and a
  two-column one.

  **In a gloss** it goes at the end of the *object* line — with a space in
  front of it or glued to the last word, either reads — and is set level with
  the object tier at the same column — not under
  the free translation, and not as a column of the grid, which would push the
  gloss tier beneath it. On any other tier it is an error: a gloss is a
  translation, and a label on a gloss tier has nothing to align with; so is
  one in the middle of a line, which has nowhere to go.

  **Spoken form.** `[CP]` is an abbreviation a screen reader can neither expand
  nor usefully spell. `\SetAnnotSpoken{[CP]}{complementizer phrase}` registers
  one for a label used throughout, and `\exannot[⟨phrase⟩]{…}` gives one to a
  single annotation; under tagging it reaches the structure tree as a `Span`
  carrying `/Alt`, so the page still shows `[CP]` and copy-and-paste still
  yields `[CP]`. Same arrangement as `\DeclareJudgment[spoken=]`. An annotation
  with no spoken form gets **no** `Span`: an `/Alt` repeating the text it
  replaces is noise in the tree. `/Alt` rather than `/E` (the brackets are not
  part of the abbreviation, and what wants saying is a gloss of the label, not
  its letters), rather than `/ActualText` (which would take the brackets out of
  copy-and-paste), and rather than `Aside` (a block element, which cannot sit
  inside the paragraph the annotation shares with its example).
- New: `[langsci]`, the `\ea` … `\z` front-end of `langsci-gb4e` (Language
  Science Press's fork of `gb4e`). `\ea` opens an example or, inside one, the
  next level down; `\z` closes whatever the matching `\ea` opened; the depth is
  read off the nesting instead of being spelt out. `\eal` … `\zl` is the
  head-and-list shape, `\eas` … `\zs` the one that will not break across a
  page, and the option brings `\gllll` through `\gllllllll`
  (four to eight gloss tiers) with it. It implies `[gb4e]`, so `exe`, `xlist`
  and `\ex` come along. Manual §3.6; `tests/langsci.tex`,
  `tests/langsci-mixed.tex` and `tests/langsci-ua.tex` (veraPDF and the
  structure depths) exercise it, and four `EXPECT_ERROR` cases pin the refusals.

  What it is for is **migration**. Combined with `[lazy]` both syntaxes are
  live at once, and because they drive the same engine they share one counter,
  one label system, one set of anchors and all layout parameters -- so a
  document moves from the dot syntax to `\ea` one example at a time, and
  converting an example renumbers nothing, moves no cross-reference and shifts
  nothing on the page. `tests/langsci-mixed.tex` asserts exactly that: a
  converted example and its unconverted neighbour set their sub-examples at the
  same indents.

  **One syntax per example.** An `\a.` inside an `\ea` example, and an `\ea`
  inside an `\ex.` example or an `exe` batch, are package errors. The two
  cannot be mixed *within* one example because they open different amounts:
  `\ea` opens the level, its list and its first item together, where `\a.`
  opens a level that `\z.` closes. Mixed, the closers stop matching the
  openers, and both ways of getting it wrong typeset silently -- a `\z.` inside
  an `\ea` example leaves the example half-open and drops what follows into the
  wrong list, and a `\z` after an `\a.` leaves the letters live for the rest of
  the document. There is no principled way to pick one reading, so neither is
  picked. This does **not** narrow the within-example mixing that `[lazy,gb4e]`
  documents and `tests/gbfour.tex` asserts (`\a.` inside `exe`, an `xlist`
  inside `\a.`): those two agree about what `\z.` means, which is precisely
  what `\ea` does not. A footnote is a new stream, so a footnote hung on an
  example written one way may hold examples written the other.

  Two deliberate departures from `langsci-gb4e`. Its `\ea` sets examples ragged
  right and this one does not, because in a half-converted document that reflows
  every converted example against the ones still to come and a page diff can no
  longer tell a mistake from a conversion; `\ExRaggedRight` asks for the
  original behaviour, and `\eanoraggedright` / `\ealnoraggedright` are provided
  for source compatibility. And `[legacy,langsci]` is a package error rather
  than an approximation: `[legacy]` reproduces `linguex`'s geometry to the value
  and `langsci-gb4e` has its own, so the combination asks for two answers to
  every length in the package.

  An `\ea` that is never closed is a named error, not the kernel's
  `\begin{list} ... ended by \end{document}`. It is the one mistake this
  syntax makes easy and the other two cannot make at all -- an `\ex.` example
  ends at a blank line and an `exe` batch at its `\end`, but an `\ea` example
  ends at a `\z` and at nothing else -- so it gets the treatment a stray `\a.`
  already had: the message names `\ea` and the line it stood on, and the
  example is then closed so the document finishes with that error alone.
- Fix: an item may open with a literal ellipsis. `\ex` peeks for the period
  that opens the dot syntax, and the peek could not tell it from a period the
  writer meant as prose: `\ex ... she doesn't believe that it's gluten-free.`
  was read as `\ex.` and the ellipsis eaten. Reported from EISS 15, where a
  continuation-style example -- a mother item sets a context and each sub-item
  resumes the sentence -- opens every item with one, and which `langsci-gb4e`,
  whose `\ex` never peeked, had always set as text. Under `[langsci]` the
  document did not build at all; under `[lazy,langsci]` it built, and did
  worse: the peek ate one dot, handed the rest to the dot syntax, and promoted
  the sub-item to a *new top-level example*, so the `\z` that should have
  closed the group had no level to close and the list was left open at
  `\end{document}` -- a lost dot, a renumbered example and three errors from
  one ellipsis. The instrument was wrong rather than the branch: `\@ifnextchar`
  skips spaces, and the space it skips is exactly what separates the two
  cases. TeX's scanner has already eaten the space after the control word, so
  `\ex. body` reaches the peek as `. <space> b`, `\ex ... text` as `. . .`, and
  `\ex. ...body` -- dot syntax whose body opens with an ellipsis -- as
  `. <space> . . .`; the test is therefore what follows the first period
  *immediately*, which is `\futurelet` and not `\@ifnextchar`. Deciding it
  with the latter fixes the first two and breaks the third, trading one
  misparse for another. `\exg.` and the `\a.`-`\f.` shorthands expand to
  `\ex.` plus a control sequence and take the dot branch as before. One form
  stays ambiguous and is documented at the definition: `\ex....body`, with no
  space, reads as prose -- the spaced form says the same thing and this one
  cannot. `tests/langsci-ellipsis.tex` carries the reported example and pins
  all three of what the peek damaged: the dots survive, both sub-items stay
  lettered members of their group, and the group is one example and not two.
- Fix: an example whose last line is full is no longer followed by an empty
  line. With `hyperref` loaded, an example's destination is hung off
  `\refstepcounter`, and in horizontal mode that is a zero-width box added to
  the line being built -- so stepping the counter before the previous item's
  paragraph was closed left the *next* example's anchor at the end of the
  *previous* example's last line, behind the space that ended the source line.
  On a full line the breaker took that space and gave the anchor a line of its
  own: a hole between (b) and (c) with nothing in the source to explain it,
  reported from a LangSci paper. The same misplacement put the destination on
  the wrong side of a page break, so a `\ref` to the example below one could
  jump a page short. All three item levels stepped their counter that way and
  all three are fixed; `tests/langsci-hole.tex` asserts, at each level, that a
  full item sits as far from its successor as from its predecessor.
- Fix: `\z.` closes the `\glt` language span when it pops a sub-level. An
  example exit has to close that span while the translation paragraph is still
  the current one; `\lx@bodyend` and the `exe`/`xlist` ends all did, and this
  third exit did not. A `\glt` under `\GlossTransLang` inside a roman level,
  popped by `\z.` with the example continuing after it, therefore left the span
  open across the list close: **veraPDF failed all three profiles**, nothing
  showed on the page, and every structure assertion in the suite still passed.
  Found while building the `\ea` front-end's own exit, which had it right from
  the start; `tests/ua.tex` now carries the shape, and its header records the
  three things about it that are load-bearing.
- New: the rest of the `langsci-gb4e` surface, under `[langsci]`, and the
  criterion for it is **what works upstream** rather than what upstream's two
  documents describe. `\eal`…`\zl`, `\eas`…`\zs`, `\zllast`, `\eafirst`,
  `\ealnoraggedright`; the sub-list numbering variants `xlista`, `xlisti`,
  `xlistn`, `xlistA`, `xlistI`; `\examplesroman`, `\examplesitalics`;
  `\attop`, `\atcenter`, `\xbox`, `\nobreakbox`; `\gblabelsep`, `\subexsep`,
  `\singlegloss`, `\nosinglegloss`, `\twodigitexamples` and its siblings;
  `\glll`…`\gllllllll`, `exe`/`xlist`, `\exi`, `\exr`, `\exp`, `\sn`,
  `\xref`, `\xxref`, `\exewidth`, `\judgewidth`, `\zlast`,
  `\eanoraggedright`, the eight font commands, `\nogltOffset`/
  `\resetgltOffset`, and the four package options `[nojambox]`,
  `[manualexewidth]`, `[lowerpenalty]`, `[nocgloss]`. Manual §3.6.

  This **reverses the decision taken earlier in this cycle**, which was to
  provide the documented surface alone -- the LangSci author guidelines and
  the `langsci-gb4e` manual -- and to refuse the two dozen names in neither.
  The reasoning was that a smaller surface entirely backed by documentation
  is worth more than a larger one that is half hearsay. What settled it the
  other way was a LangSci paper whose examples are written with `\eal` …
  `\zl`: the undocumented half of `langsci-gb4e` is in use in real sources,
  a document being ported does not care which half of the package its author
  reached for, and a name with a working behaviour upstream now has one here.

  **Two names are still refused**, and only because there is nothing to copy.
  `xlistabr` labels every item `(xnumii.` upstream -- its `\@xlist` binds the
  numbering slot to a single token and `xlistabr` hands it four -- and
  `qlist` names a counter (`xnum`) that no file in the package defines, so it
  raises "No counter 'xnum' defined" and labels every item ".". Neither has
  ever worked in a released version; providing them would not be
  compatibility but the invention of a meaning for a name whose only
  precedent is a bug. Both are *defined*, and defined to raise an error
  saying what to write instead, so a document using one stops at the line
  that has to change rather than at "Undefined control sequence".

  **Four deliberate differences from upstream** among what *is* provided.
  `\exp` keeps both meanings -- it is the LaTeX kernel's math operator as well
  as an item command, and upstream simply takes it, so a paper writing
  `\exp{ex:5}` silently loses `$\exp(x)$` and in fact stops compiling; here
  the mode decides. `\exp`'s prime and `\atcenter` are written in text mode,
  upstream having them in math, which would put a `Formula` element in the
  tree. `\eas` boxes its example in a `minipage` and not in a `tabular`,
  which would wrap running prose in a `Table` element. `\judgewidth` and
  `\subexsep` warn instead of acting: the first sets the width of a reserved
  judgment column, and a judgment here hangs into the label gutter and
  reserves nothing, which is why marked and unmarked examples align; the
  second sets a label separation for the sub-levels alone, and this package
  has one `\Exlabelsep` for every level. `[nocgloss]` is accepted and
  reports: there is no bundled `cgloss` here to withhold, and what each of
  `\exg.`, `\altg`, `\GlossTierLang` and `\lpzg` should then do is in
  `doc/DEFERRED-DECISIONS.md` rather than guessed at. And the font commands
  are declarations, as upstream's are -- the manual writes them with an
  argument (`\exfont{\itshape}`), but its own `\exfont` takes none, so that
  spelling does nothing there either.

  Three things were found by the tests rather than by reading upstream.
  `\exp` was silently replaced under `\DocumentMetadata`, where the tagged-math
  code re-declares the operator after this package is read, so the dispatch is
  installed at begin-document as well as at load. `\lx@custom@item@here` was
  dead code until `\exi` was routed through it, so a mutation making it step a
  counter changed nothing and the suite stayed green over it. And the fix for
  the `xlistabr`-shaped problem that seemed obvious -- `\let` to `\def` --
  does not work, the numbering slot being used as `\@xs<N>{counter}`.
- New: under `[langsci]`, a plain `\ref` prints the bare number, as
  `langsci-gb4e` does. **This changes what an existing `[langsci]` document's
  cross-references print**: `\ref{ex:x}` gives `1` where it gave `(1)`, at
  every level and on the footnote series (`1`, `2a`, `2b-i`, `i`).

  The two ecosystems disagree about where the parentheses live. `linguex`
  puts them in the counter format, so `\ref` gives "(1)" and prose says "as
  in \ref{ex:x}"; `langsci-gb4e` puts them in the example's label and leaves
  the counter bare, so `\ref` gives "1" and prose says "(\ref{ex:x})" -- or
  `\xref{ex:x}`, which is why upstream's `\xref` is `(\ref{#1})`. Neither is
  more correct, and a document carried from the second convention to the
  first prints "((1))" on every cross-reference, which is what this fixes.
  The convention therefore follows the syntax the document is written in.

  Everything this package prints *itself* is parenthesised whatever the
  convention -- the example's own label, `\Next` and its family, `\xref`,
  `\xxref`, `\exr`, `\exp`, `\refrange` and `\Refrange` -- so only a plain
  `\ref`, and the `\cref` that follows it, change. `\ExParenRefs` and
  `\ExBareRefs` ask for the other convention by name, in the preamble or
  mid-document; the manual itself is a `[langsci]` document that writes
  `\ExParenRefs` in its preamble, which is the whole of what a paper in the
  same position has to do. `tests/langsci-refs.tex` asserts both
  conventions, both switches and the fact that the label keeps its
  parentheses -- the last because the tempting one-line version of this
  change (drop them from `\theExNo`) takes the printed number with them.
- New: a documented API for building a syntax front-end or a geometry mode
  on this package's machinery, without touching its internals. The seam was
  already there -- the dot syntax and the `exe`/`xlist` environments are
  thin layers over one shared engine, and neither the numbering, the label
  boxes, the judgments, the glossing nor the tagging knows which called it.
  What is new is that the seam has names: `\lx_example_begin:`,
  `\lx_body_collect:N`, the `\lx_item_...` family, `\lx_sub_push:`,
  `\lx_judgment_...`, the list funnel and the tagged-Span helpers
  (Protocol A), and `\lx_mode_new:nn` / `\lx_mode_select:n` for a
  geometry beside `[legacy]` (Protocol B). Documented in the manual's new
  §11, including the four invariants a front-end has to respect, and
  exercised by `tests/frontend.tex`, which builds a working front-end out
  of the public names alone and is validated with veraPDF.

  **No behaviour changes.** Everything user-facing is what it was: the
  public names are aliases of the internals they expose, the `[legacy]`
  branch became a named mode selected the same way, and the three example
  documents rebuild to byte-identical pages with an identical structure
  tree. Nothing in an existing document needs to change, and no internal
  spelled `\lx@...` was renamed or removed -- but those remain private and
  unsupported, which is the point of publishing the others.
- Fix: the relative references take their sub-example argument again.
  `\Last[b]` is `linguex` syntax for "letter b of the previous example",
  but `\Last` was declared without an optional argument, so the brackets
  were never an argument at all -- they fell through to the page and
  `\Last[b]` set "(1)[b]" instead of "(1b)", with a clean compile and no
  warning. `\Next`, `\NNext`, `\LLast`, `\TextNext` and the whole
  `p`-prefixed family take it too, and the part is joined with
  `\firstrefdash`, so a relative reference and a `\ref` to a `\sublabel`
  spell the same example alike in both modes: `(1b)` by default, `(1-b)`
  under `[legacy]`. Inside a footnote the part rides on the footnote's
  roman series, except under `\TextNext`, which keeps pointing at the main
  one. Covered by `tests/relrefs.tex` (which until now was a case file the
  suite never ran) and, for the dash, by `tests/legacy.tex`.
- Fix: a judgment mark no longer disappears from an `\altn` alternative.
  `\altn{est}{*sont}` printed "sont". The stack is a `tabular`, its rows are
  separated by `\\`, and `\\` in a `tabular` is `\@arraycr`, which opens with
  `\@ifstar` -- so an alternative beginning with `*` handed the row separator
  its own starred form `\\*` and the mark was eaten as syntax. Only the
  second and later alternatives were affected, since nothing precedes the
  first, and the compile was clean. The separator is now `\\[0pt]`, which
  settles the star scan before it reaches the mark and also disarms the same
  trap for an alternative opening with `[` (`\altn{[+wh]}{[-wh]}`, which
  raised "Illegal unit of measure"). `[0pt]` is a true no-op: it struts the
  row to the depth `\@arstrut` already gives it. Covered by `tests/altn.tex`.
- New: under `[phantomalign]` (or `\GlossPhantomAlign`), a judgment mark in
  an `\altn` stack hangs to the left instead of displacing the alternative it
  marks. Left-aligned, `\altn[l]{est}{*sont}` used to put `est` under the
  star and `sont` a star-width to its right, so the one pair of words being
  contrasted was the one pair not aligned. The mark now goes in a
  right-aligned gutter column of its own -- inside the braces, since it
  judges its own alternative and not the stack. Where rows carry marks of
  different widths the gutter takes the widest, so every mark sits flush
  against the words. Nothing is inserted between a mark and its word: that
  distance is the font's own, exactly what typing `*sont` gives, and only
  the *other* rows move, so an aligned stack is no wider than the same stack
  unaligned. A marked stack also tucks `\AltJdgTuck` (new, default
  `0.45em`) closer to its opening brace, into the hollow the brace's arm
  leaves as it curls away from the content between its tips: at 10pt that
  takes the opening brace from 6.8pt to 2.4pt from the mark, and from
  11.5pt to 7.0pt from the unmarked rows -- 6.4pt being what an unmarked
  stack has, so adding a judgment no longer displaces the words. Only the
  opening gap moves, so the closing brace rides along and the distance to
  the preceding word is unchanged; and only a stack that carries a mark
  tucks. `0pt` restores the untucked layout. Not clamped, like the other
  brace lengths -- but a tuck deeper than `\AltBraceWidth + \AltBraceSep`
  pulls the stack through the brace, and overlapping ink is silent in TeX,
  so the package writes one warning naming the value and the threshold and
  then uses the value anyway. Reported once per document, and only for a
  stack that carries a mark. Covered by `tests/alttuck.tex`. The marks recognised are `\GlossPhantomChars`, and a
  stack in which no alternative carries one is set exactly as before, so the
  option never moves a stack that has nothing to align. Documented in the
  manual's §7.1 and §5.7, covered by `tests/altn-phantomalign.tex`.
- New: the same for `\altg`, on the tier that carries the object language.
  A judgment is a claim about the object language, and a gloss is a
  translation of it rather than something that is itself grammatical or not,
  so a mark typed in a *gloss* alternative stays where it was put; the
  object call and a solo `\altg` hang theirs. The gloss stack needs no help
  to follow the widened object stack: `\__lxp_altg_shape_obj:` derives the
  gloss cell's indent from the object emit's own width, so the paradigm
  stays a two-column block. Documented in the manual's §7.2, covered by
  `tests/altg-phantomalign.tex`, which asserts the asymmetry in both
  directions.

  This corrects a claim made in the `\altn` entry above as first written,
  and in the source comments and manual with it: that `\altg` could not
  have this at all, because its two calls "must keep the same width" and a
  gutter on one tier would tear them apart. They need not — the two calls
  sit side by side in one gloss column, not stacked — and nothing had to be
  kept in step by hand. The wrong reason is recorded next to the right one
  in `linguexx.sty`, because it is the kind of invariant that looks true
  from the tier-per-call structure alone.
- Fix: `\#` and `\%` are recognised as leading marks by the aligner, and
  `\GlossPhantomChars` now takes commands as well as characters.

  These are one change because the first is not fixable without the second.
  The set was a string of characters and the peel rejected anything longer
  than one of them, so a control sequence could never match -- while a bare
  `#` is a macro parameter character and a bare `%` opens a comment, which
  makes `\#` and `\%` the only spellings a document can contain. `\ex. #Ceci`
  is a TeX error, not a linguexx one. So the two marks the documented default
  set named were unreachable, and the package disagreed with itself: `\ex.
  \#Ceci` hung its mark in the margin, because the judgment scanner has
  always matched `\#` and `\%` by meaning, while `\altn{un nez}{\#le nez}`
  left the same mark sitting inside the alternative. Reported from a real
  document.

  The set is now a list of whole marks rather than a string of characters,
  and an entry may be either. `\GlossPhantomChars` takes them without
  separators -- one token is one mark -- so `\GlossPhantomChars{*?([<\#\%\dag}`
  keeps the default set and adds `\dag` to it. A command not in the set is
  still not a mark, and a *braced* one is not a mark either: `{[}stem` stays
  as typed, since bracing the bracket is how you keep it out of exactly this
  scanning.

  The two peelers became one. They had been written out separately -- the
  gloss aligner peeling characters off a stringified word, the stack builder
  taking tokens -- and had already drifted: they disagreed about `{[}stem`,
  and about `\#` they were wrong in the same way for different reasons. One
  copy cannot drift from itself, which is the argument `\__lxp_alt_build:NNnnN`
  already makes for the stacks.

  Unifying them exposed a third bug underneath, which nothing could have
  found before: the gloss aligner retrieved the object word with
  `\exp_args:NNf`, and f-expansion does not stop where it looks as though it
  stops -- it evaluates `\seq_item:Nn` and then carries on into the word and
  expands its leading token too. For a *robust* mark that is one step too
  many: `\dag` becomes `\protect\dag`, and the peel is offered `\protect`. It
  was invisible while the set held characters, which cannot be expanded, and
  it survived the first half of this change because `\#` and `\%` are not
  robust in LaTeX2e. It shows up the moment a document declares a mark of its
  own that is. The word is now fetched without expanding anything, which is
  what the comment at that call site always claimed.

  Covered by `tests/phantommarks.tex`, which asserts the set on both
  consumers and in both directions -- a declared mark peels, an undeclared
  one does not -- because "recognised" and "not recognised" are each other's
  control and a peel that swallowed everything would pass half of it.
  Mutation-checked: five mutations, five kills, and the f-expansion one is
  caught by exactly the assertion written for it.

- Fix: the relative-reference links work under `beamer`, where they had
  silently switched themselves off.

  `beamer` loads `hyperref` with `implicit=false` -- it anchors its own
  `\label`s and runs its own navigation -- so `hyperref` places no
  destination at `\refstepcounter`. The links stood down on that ground, and
  it was the wrong conclusion from a true premise: nothing about
  `implicit=false` makes a destination impossible, it only means `hyperref`
  will not make one unasked. The result was that in the class most
  linguistics slides are written in, `\ref` moved and `\Last` did not --
  precisely the inconsistency the links exist to remove, left in place by the
  guard meant to protect them. Reported from a lecture deck.

  Where `hyperref`'s implicit anchors are off, `linguexx` now places the
  destination itself, at the example's `\item`, and the rest of the mechanism
  runs unchanged on names it placed rather than names it read.

  The overlays are the part with no trace on the page. A `beamer` frame is
  typeset once per slide with the example counters restored each time, so an
  example on a two-slide frame comes past twice carrying the same number.
  Left alone that is a second destination of the same name, which `hyperref`
  drops with a warning per repeat, and a second `.aux` record -- which the
  guard against *shared* anchors would read as two examples claiming one name
  and refuse, on that ground, to link the very examples in question.

  The name cannot tell that apart from a genuinely reset counter, which
  produces the same repetition and does deserve that treatment. What tells
  them apart is *where* the repeat happens: a frame's passes repeat inside
  one frame, a reset counter reuses numbers across the document. So the
  question asked is not "is this the first pass" but "have I already placed
  this name in this frame", with `\c@framenumber` -- constant across a
  frame's slides -- saying which frame that is. Outside `beamer` there is no
  such counter, the question is never asked, and every example records
  exactly as before.

  Asking about the pass was the first attempt and it was wrong twice over,
  for cases that are not exotic. An example inside `\only<2->{...}` is not
  typeset on the first pass, so its *first* appearance is on a later one,
  and a rule that skipped every later pass gave it no anchor at all --
  after which the reference naming it was reported as dangling, by the
  mechanism that had discarded its target.

  And the pass an anchor goes on has to be one where the example can be
  *seen*, which is not the pass where it is first *run*. `\pause`,
  `\uncover` and `\onslide` execute their material on every slide of the
  frame -- the counter steps, and an anchor placed there is placed on that
  slide -- and drop its ink where it is covered. So an example after a
  `\pause` runs on slide 1 and appears on slide 3, and anchoring it where
  it ran sent the reader to a slide with nothing on it to see. Nothing on
  the page showed the mistake, because on that page there was nothing
  there: it was found by clicking a reference in a real deck and landing in
  the wrong place. `\beamer@coveringdepth` is 0 exactly when the material
  is visible and counts up through nested covering, so it answers for
  `\pause`, `\uncover` and `\onslide` alike; `\only` needs no answer,
  since it does not run at all on the slides it excludes. An example
  covered on *every* slide of its frame is therefore never anchored, and a
  reference to it is reported as pointing at an example that does not
  exist -- which it does not.

  Both were caught by the person the feature was built for, on the deck it
  was built for.

  What the rule still does not catch is a counter reset *inside* a single
  frame, where the second example is skipped rather than reported. A reset
  between frames is caught, which is the form a reset takes in practice.

  Covered by `tests/relreflinks-beamer.tex` and
  `tests/relreflinks-beamer-reset.tex`, which count `.aux` records rather
  than looking at the page, and each first check that the frame really did
  produce two slides -- otherwise the overlay assertions would pass on a
  frame that never repeated. Four mutations, four kills, each for its own
  reason: dropping the dedup records the overlay repeat and loses the link;
  making the set document-wide swallows the reset and turns a withheld link
  into a wrong one; the original pass-based rule leaves the `\only` example
  with no anchor at all; and ignoring the covering depth puts the paused
  example's anchor on the same page as the one above it instead of a page
  later. That last assertion is the only one in the suite that resolves a
  named destination to a page, which needs `qpdf` -- a new dependency, since
  no poppler tool reports it.

- New: the relative references are clickable. With `hyperref` loaded,
  `\Next`, `\Last`, `\NNext`, `\LLast`, `\TextNext` and their `p`-twins link
  to the example they name, as the `\ref` they abbreviate always has. Nothing
  had to be added to the document to make this possible: `hyperref` puts a
  destination at every `\refstepcounter`, so every example already had one
  whether or not it carried a `\label`.

  What is new is everything around that. A relative reference names its
  target by *arithmetic*, so it can name a number no example carries --
  `\LLast` before example 2, `\Next` after the last one -- and a link to a
  destination that does not exist is not an error: the backend substitutes a
  whole-page destination, so the click lands somewhere plausible and wrong.
  The three engines do not even agree that this deserves a word (pdfTeX and
  LuaTeX warn, in two different phrasings; `xdvipdfmx` says nothing), so
  `linguexx` checks it itself. Every example records the anchor `hyperref`
  gave it in the `.aux`, and a reference is linked only if the previous run
  saw the anchor it wants. A reference that has no target prints exactly the
  number it always printed and is named, with its input line, in one warning
  at the end of the run -- which is new diagnostic information: until now an
  off-by-one relative reference was silent by construction. If the two runs
  disagree about the set of anchors the answer is a rerun rather than a
  report, as for `\lpzglist` and `\tableofcontents` before it.

  A number that *two* examples carry is withheld in the same way, and
  reported in its own words. `\theHExNo` is built from `ExNo` alone, so
  resetting the counter -- with `\setcounter`, or by `[legacy]`'s per-chapter
  reset in a class that has chapters -- makes two examples claim one
  `hyperref` anchor, and `hyperref` keeps the first destination of a name and
  drops the rest. That is a defect in the anchors and it is older than the
  links: a `\label` on the second example has always led to the first. It is
  not settled here, since mending it means changing what `\theHExNo` records
  and therefore what a stale `.aux` means. What the links promise is only
  that they add no wrong jump of their own to a document that has it.

  The optional sub-example part is printed but not aimed at: `\Last[b]` links
  to the example, not to its letter. The letter anchors exist, but their
  letter comes from `\alph` while the printed one comes from `\Exalph`, a
  documented hook, so in a document that renumbers its sub-examples every
  part-link would dangle -- and the part is hand-typed text that need not
  name a real letter in the first place. Landing two lines high is the better
  failure.

  On by default when `hyperref` is loaded, and only then: without it the
  mechanism costs nothing, looks up no anchor and writes no line to the
  `.aux`. `[norelreflinks]` turns it off and restores the `linguex`
  behaviour. `hyperref`'s own `implicit=false` turns it off too, since it
  suppresses the destinations this aims at. `\ref` is untouched: `linguexx`
  still only ever *uses* it, so its immunity to the order `hyperref`,
  `cleveref`, `varioref` and `nameref` load in is unchanged. Covered by
  `tests/relreflinks.tex`, `tests/relreflinks-off.tex` and
  `tests/relreflinks-reset.tex`, which read the link annotations out of the
  PDF -- a link is invisible in the rendering, which is why this was missing
  for as long as it was. Under tagging the references become `Link` structure
  elements with their `OBJR`: `tests/ua.tex` now carries one, so veraPDF sees
  it on all three engines on every run, and `examples/ua-demo.pdf` -- which
  has one in a footnote too -- still passes `ua2`, `wt1a` and `wt1r`. The
  shared-anchor defect behind the second paragraph is recorded in
  `doc/DEFERRED-DECISIONS.md`.
- Fix: the relative references no longer swallow the space that follows
  them. `\Last`, `\Next`, `\NNext`, `\LLast`, `\TextNext` and their
  `p`-twins are control words, so TeX's tokenizer discards the space in
  `\Last shows that` before any macro can see it, and the reference set
  solid against the next word: `(1)shows`. They now end in `\xspace`, as
  `linguex`'s `\printExNo` does, which restores the space before a word
  and still omits it before punctuation, so `\Last, but` stays `(1), but`.
  The defensive `\Last\ ` and `\Last{}` that documents written against
  the old behaviour contain are unaffected -- `\xspace` recognises both.
  Covered by `tests/relrefs.tex`.
- Fix: a modifier inside a `\lpzg` label now *adds* to the small capitals
  instead of replacing them. `\lpzg{\textbf{m}.pl}` -- the way one picks the
  cell of a paradigm the surrounding discussion is about -- came out as a
  bold *lowercase* "m" under xelatex and lualatex: Latin Modern has no bold
  small capitals in any encoding (there is no bold `lmromancaps`, and
  `lmodern` under pdflatex has the same gap), and NFSS's fallback keeps the
  series and drops the shape. Silently: the log says "Font shape
  `TU/lmr/bx/sc' undefined", which reads like a remark about a font, while
  what is lost is the one feature that marks a category label as a label.
  `\lpzg` now decides per *leaf* of the label, and by asking the font that
  was actually selected rather than by keeping a list of families: it
  compares the font `\scshape` lands on with the upright one, a
  substitution being invisible in `\f@shape` and plain in `\fontname`. Where
  the shape is real -- pdflatex's default `cmr`, any OpenType face with
  `smcp` in its bold weight -- the label is set with `\textsc` exactly as
  before, byte for byte. Where it is not, the capitals are made: uppercased
  and set at `\LpzgCapsScale` (new, default `0.75`) of the size, in the font
  the modifier chose, so the boldface survives and the height still matches
  the real small capitals beside it. Under active tagging the made capitals
  carry their own marked content with an `/ActualText` of the letters as
  written, so the text layer still says `m.pl`; `tests/ua.tex` has such a
  label now, and veraPDF passes it on all three engines. `\textbf`,
  `\textit`, `\textsl`, `\textup`, `\textmd`, `\textrm`, `\textsf`,
  `\texttt`, `\textnormal` and `\emph` are recognised inside a label.
  Measured from the ink in `tests/lpzg-mod.tex`: the modified label is as
  tall as the plain one and carries half as much ink again.
- Fix: the same label is now parsed *through* its markup. The first piece
  of `\lpzg{\textbf{m}.pl}` reached the Leipzig table as `\textbf {m}`,
  which expands to nothing, so the `/E` spoke the markup, `\lpzglist` had an entry named
  after a control sequence, and `\lpzgcheck` warned about a key the author
  cannot find in the source. The label is purified before it is parsed;
  printing is unaffected.
- Fix: the brace's tip. A cusp has two tangents and the outline was
  offset along one of them, so the two halves of the brace met in a pinch
  with a flat wedge hanging off it -- invisible at reading size,
  unmistakable at thirty times it. The point is now where the two outer
  edges actually meet, and the notch inside it where the two inner edges
  cross; the inner curves END at that crossing rather than running past it
  and doubling back, which is what left a white star sitting in the point
  on the first attempt at this. Both are clamped, and to different
  amounts, because Computer Modern's tip is *blunt* -- cut off with a
  small flat -- while its notch is a clean deep V: one pen-half out, two
  in. The clamp also keeps a short brace's tip from running away, the
  corner length being a quarter of the span.
- Two brace spacings retuned for the weight the brace now has, both of
  them defaults that were measured against a hairline and do not survive
  it. `\AltBraceOuterSep` (the gap between a brace and the words around
  it) goes from `0.7em` to **`0.35em`**: 0.7em is most of a word space on
  top of the one the source already has, and with a brace of the type's
  own weight the stack read as something held at arm's length from the
  words it belongs to. What has to be cleared there is the tip's overflow
  past its box -- about 1.1pt at 11pt -- so the halved value still leaves
  2.8pt of air. And `\AltJdgTuck` (how far a stack with a hanging judgment
  tucks into the hollow of its opening brace, under `[phantomalign]`) goes
  from `0.45em` to **`0.2em`**: at 0.45em the mark clears the new outline
  by 0.24pt, which is to say it touches it. 0.2em restores the clearance
  the hairline gave (2.3pt at 10pt) and, unlike any deeper value, holds it
  across sizes -- 2.3pt from 10pt to 20pt, where 0.3em falls from 1.3pt to
  0.3pt, because the pen scales with the font and `\AltBraceAmplitude`
  does not. The cost is the other half of the tuck's bargain: a judged
  stack's words now sit about 3pt further in than an unjudged stack's,
  where at 0.45em they sat within half a point. `tests/altn-phantomalign`
  measures the air between mark and brace, which is the one fault neither
  the compiler nor the text layer can see.
- The brace of `\altn` and `\altg` is drawn as a filled **outline**, and
  with a typographic weight. It was TikZ's `brace` decoration stroked at
  0.4pt -- a hairline beside the type it stands next to, and one width
  everywhere, which a real brace does not have. Measured off Computer
  Modern's own `\left\{` at 11pt: the stem is a constant **1.20pt**
  (`0.11em`, the new `\AltBracePen`), tapering to 0.42pt at the two
  terminals and narrowing to 0.72pt at the cusp. The outline follows the
  same skeleton as before -- pgf's brace decoration, four cubics and two
  lines -- offset to either side by half the pen and tapered at those four
  places.

  It costs what it draws: the geometry is some thirty floating-point
  evaluations, so it is **cached** by side, span, amplitude, width and
  pen, and a document draws the same few stack heights over and over.
  Measured on 300 braces under pdflatex: 1.52s with the cache warm against
  0.87s for the old decoration, and 2.47s when every brace is a different
  height. The `decorations.pathreplacing` library is no longer loaded --
  a filled path needs nothing but `tikz` itself.

  `\AltBraceWidth` and `\AltBraceAmplitude` are unchanged and still
  reshape both commands' braces; `\AltBracePen` joins them.
  `tests/altn.tex` measures the pen off the rendered shaft, where a
  hairline reads 0.4pt and the default reads 1.20pt.
- `graphicx` is no longer required. The line was annotated "`\scalebox`
  etc." and the package calls `\scalebox` nowhere -- nor `\resizebox`,
  `\rotatebox`, `\reflectbox` or `\includegraphics`, in this version or in
  any committed one: the first commit already carries the requirement and
  already has no caller. Nothing changes for a document, because `tikz`
  loads `graphicx` itself, which is why it went unnoticed; what changes is
  that the package's declared dependencies are now the ones it has. They
  are `tikz` (with `decorations.pathreplacing`, for the drawn brace of
  `\altn` and `\altg`), `xspace`, and the expl3 layer in the kernel.
- `\lpzg` may stand in a section title. A heading is expanded into a PDF
  string for the bookmark, and a command hyperref has not been told about
  is dropped there with "Token not allowed in a PDF string (Unicode):
  removing `\lpzg'" -- on every run of every document that glosses in a
  heading. The label survived that by accident (dropping the command
  leaves its argument behind); the log did not. `\lpzg` is now declared to
  hyperref as its own argument, so the bookmark reads `3sg.pst` -- the
  same letters an `/ActualText` hands out, since a PDF string has no small
  caps to carry. `\altn`, `\altg`, `\Next` and its family are deliberately
  *not* declared: a stack of alternatives and a number the list machinery
  works out have no one-line reading, and hyperref's own treatment of them
  -- dropped, with the warning that says so -- is the honest outcome.
  `tests/cedilla.tex`, the case about what survives a title, reads the
  bookmark back out of hyperref's `.out`.
- Fix: `\exg.[label]` takes the custom label `\ex.[label]` takes. `\exg.`
  expands to `\ex.` plus the gloss head, so the bracket was no longer the
  first thing the label peek saw: the label came out as the first word of
  the object line and the example took a number of its own -- which made
  `\exg.[\ref{ex:1}]`, the way one repeats a glossed example under the
  number it had, unwritable. The bracket is now read before `\ex.` is and
  handed on in the one position that peek looks at.

  **It must be written against the command.** After `\exg.` a bracket that
  a space separates is the first word of the object line -- `[` is one of
  the openers `\GlossPhantomChars` lists, and phantom alignment exists to
  hang it in the gutter -- so the space is the only thing that tells
  `\exg.[(7')] Der Hund ...` from `\exg. [DP der Hund] bellte.`, and a
  space-skipping `\@ifnextchar` would take the constituent bracket of every
  such gloss for a label. `\ex.` keeps `\@ifnextchar`: it has no object
  line for a bracket to open, and its spaced form is documented behaviour.
  `tests/numbering.tex` pins both halves.
- Fix: an abbreviation key that is not ASCII is typeset as the character it
  is. A key is stored and compared as a string, and under pdflatex a string
  is BYTES, so `\lpzgadd{abß}` printed `abÃ§` -- the two UTF-8 bytes of `ß`
  sent to the T1 slots of `Ã` and `§`. Only the *printing* was ever wrong:
  the `.aux` round trip, the `/E` expansion text (tagpdf reads the bytes as
  the UTF-8 they are) and the warnings in the log were all correct, which is
  how it survived. The bytes are now turned back into characters where they
  are set, and nowhere else -- identity, comparison, sorting and the `.aux`
  keep the string they have always had. Re-tokenising is what does it, with
  every ASCII special forced to catcode 12 first: a key is text, not markup.
  The label column of the list is measured from the same readable form, so
  it no longer reserves a glyph per byte. Unchanged for ASCII keys, to the
  pixel.
- Fix: the spellings of such a key agree. `\SetLeipzig{f\'em}`, `\lpzg{fém}`
  and `ignore={f\'em}` are one key now, in any combination: every key is
  normalised the same way (purified, then a string), where `\SetLeipzig`
  used to store what it was handed. Two spellings of one abbreviation gave
  a list with an unexplained entry beside an unused declaration, and no
  warning that named the reason. `tests/lpzglist.tex` carries the key in
  all three spellings.
- Fix: punctuation glued to the object call of an `\altg` is set after the
  paradigm instead of inside it. A paradigm is one block spanning both
  tiers and its closing brace is drawn by the *gloss* call, so a
  sentence-final period written where a period is written --
  `\altg{posvadjal-i}{*posvadjal-e}.` at the end of the object line --
  landed immediately after the object stack: inside the braces, in the
  gutter between the object and gloss columns, reading as a period on the
  first alternative. The object call now takes the punctuation with it and
  the gloss call sets it down past the brace and level with the middle of
  it -- the one height that belongs to both tiers, the sentence it ends
  being the paradigm's rather than the first alternative's -- with the
  brace's own `\AltBraceSep` in front of it, since the tip points straight
  at it and a period flush against that reads as part of the brace.
  Only characters from `.,;:!?)]` and only with no space between: anything
  after a space is a gloss column of its own and already fell past the
  paradigm. `\altn` needs none of this -- it draws both braces itself.
  Covered by `tests/lpzg-mod.tex`.

## 1.2
- `\altn`'s spoken `/Alt` now expands a Leipzig abbreviation, as `\altg`'s
  already did: `\altn{a \lpzg{pl} of cats}{a dog}` is announced as "a plural
  of cats or a dog" rather than "a pl of cats or a dog". Only the spoken form
  changes -- the stack still prints the small-cap abbreviation, and that
  abbreviation still carries its own `/E` expansion inside the stack (unlike
  in an `\altg` stack, which sets `\lpzg` plain). Simple keys only, as in
  `\altg`: a compound or unknown key is spoken as printed.
- Fix: `\z.` is now usable inside an `exe` batch. Mixing the syntaxes is
  documented, so an `\a.` inside `exe` legitimately opens a sub-level -- but
  `\z.`, the only thing that could close it again, raised "`\z.` outside an
  example": the command was gated on a flag only the dot syntax sets. It is
  now gated on the open sub-level itself, so `\a. ... \z.` inside a batch
  returns to the main level and the next `\ex` is a main-level example
  again. (With the `\z.` omitted that `\ex` is still silently demoted to a
  sub-item: `\ex` continues at the *current* level, which is the rule
  `xlist` documents, and `\z.` is now the escape.) Only the branch that ends
  the example stays dot-syntax-only; a `\z.` at the main level of a batch is
  a package error saying to end the batch with `\end{exe}`.
- Fix: hyperref anchors for sub-examples in footnotes no longer collide with
  main-text ones. `\theSubExNo` (the printed label) branches on
  `\if@noftnote`, but `\theHSubExNo`/`\theHSubSubExNo` (the anchors) built
  their name from `ExNo` unconditionally, so a sub-example "a" in a footnote
  and one under main example 1 both claimed `lxex.1.a`. hyperref keeps the
  first destination of a name and drops the rest, so a `\ref` to the
  footnote sub-example linked to the main-text one -- a wrong *link*, never
  a wrong number, which is why it stayed invisible. Footnote sub-examples
  now anchor on the footnote series, `lxfnex.<FnExNo>.<letter>` and
  `lxfnex.<FnExNo>.<letter>.<n>`, matching `\theHFnExNo`. Main-text anchors
  are unchanged.
- Fix: a stray `\a.` in prose, with no example of either kind open, is now a
  package error naming itself. It used to open a list and a `\begingroup`
  that nothing ever closed, and the document died much later with
  "`\begin{list} ended by \end{document}`" -- a message naming neither `\a.`
  nor the line it stood on. `\a.` inside an `exe` batch is unaffected: it
  reaches the same code path legitimately, and stays legal.
- Fix: a trailing or doubled period in a `\lpzg` label no longer records an
  empty abbreviation. `\lpzg{sg.}` splits into `sg` and an empty piece, and
  the empty piece was recorded as a used key like any other, so `\lpzgcheck`
  reported "No expansion known for" nothing at all -- a warning naming a key
  the author could not find in the source -- and the `/E` expansion carried
  a trailing space. Blank segments are now skipped; the real pieces beside
  them are recorded exactly as before.

## 1.1
- `\lpzglist`: the list of the abbreviations the document actually uses, each
  with its full form. Every label passed to `\lpzg` is recorded piece by piece
  (`\lpzg{3sg.pst}` contributes `3`, `sg` and `pst`), written to the `.aux`,
  and reported by `\lpzglist` wherever it stands -- in the front matter as
  readily as at the end; when the list is a run behind, a rerun is requested.
  `\lpzgadd{erg,abs}` registers abbreviations used outside `\lpzg`.
  Abbreviations with no known expansion are omitted with a warning naming them
  (`unexplained=keep` lists them unexplained instead). Customisation, per list
  via `\lpzglist[...]` or document-wide via `\lpzglistsetup{...}`: `style`
  (`list`/`inline`), `sort`, `include` (`used`/`all`), `ignore`, `add`,
  `unexplained`, `title`, `titlestyle`, `sep`, `itemsep`, and `format` (the
  one-shot form of `\lpzglistentry`, `#1` the abbreviation, `#2` its full
  form). Under tagging the default style is a real tagged list (`L → LI → Lbl
  → LBody`) whose labels keep their `/E` expansion and stay flush left;
  verified with veraPDF on `examples/ua-demo`. `\lpzg` inside an `\altg` stack,
  which prints plain, now counts as used as well.
- Phantom bracket alignment for interlinear glosses (opt-in, off by default).
  When an object word opens with a run of brackets, parentheses or judgment
  marks, the gloss word below it can be padded by a `\phantom` of that run --
  set in the object-line font -- so its first real glyph sits under the object
  word's first real glyph rather than under the mark. Enable with the package
  option `phantomalign` or `\GlossPhantomAlign` (`\GlossPhantomAlignOff` to
  scope it); `\GlossPhantomChars{...}` sets the leading characters that count
  (default `*?#%([<`). For material the automatic scan cannot see (a
  macro-wrapped bracket) or a hand-picked target, `\GlossPhantom{material}`
  is a manual override: placed at the front of a gloss word it pads it by an
  invisible box the width of `material`, set in the object-line font. The
  phantom ships no ink and no marked content, so PDF/UA tagging is unchanged
  (verified with veraPDF on `examples/ua-demo` and on a `\GlossPhantom`
  build). Alignment survives a size change of the gloss tier (e.g.
  `\footnotesize`). `\altg` alternative columns are not covered.
- Fix: the sub-example letters and the kernel accent commands `\b`, `\c`,
  `\d` now coexist, **everywhere, including inside the same example**:

  ```latex
  \ex. \a. Fran\c cois est fatigu\'ee.   % \c = cedilla
       \b. \c Ca c'est chiant.           % \b = sub-example, \c = cedilla
  ```

  Each letter dispatches on what follows it: a period is the sub-example
  command, anything else (`{`, a letter, a space) is the accent, i.e.
  whatever the letter meant before linguexx touched it. `\c{c}` and an
  inputenc-decomposed "ç" (literally `\c c`) both take the accent branch.
  Previously, under `[lazy]` (the default), all six of `\a`-`\f` were
  redefined globally and permanently, so `\c{c}` or "ç" anywhere in the
  document raised "Use of `\c` doesn't match its definition", and in a
  hyperref `\section` title the accent was dropped *silently* from both the
  printed heading and the PDF outline entry. The hooks are `\protected`, so
  hyperref's `\edef` over a title leaves them alone instead of running their
  lookahead; only `\a` is held globally (it must be able to *open* a level,
  and it is no accent at all), while `\b`-`\f` are hooked just where a
  sub-level is reachable. Under `[gb4e]` alone nothing is redefined, as
  documented. `\e`/`\f` without a period, which the kernel does not define,
  now give a named package error instead of "undefined control sequence".
- Fix: `\end{exe}` now closes any sub-level opened by an `\a.` inside the
  batch (`\lx@closesubs`). It previously closed only the main list and
  leaked `\lx@subpush`'s `\begingroup`, leaving `\lx@subdepth` stuck at the
  sub-level for the rest of the document.
- `\glt` (the free translation) gains two hooks, both opt-in and both
  leaving the default output and the default tag tree byte-for-byte as they
  were:
  - `\GlossTransStyle` styles the translation. It is a *declaration*
    (`\renewcommand\GlossTransStyle{\itshape}`), not a one-argument command
    like `\GlossTierFont`, because the translation is delimited by the end of
    its paragraph rather than by braces; a declaration also scopes itself to
    the example, so it neither reaches the gloss tiers above the `\glt` nor
    leaks past the end of the example.
  - `\GlossTransLang{code}` marks the translation's language, as
    `\GlossTierLang` does for a gloss tier: under tagging the translation is
    wrapped in a `Span` carrying `/Lang`, so a screen reader pronounces it
    correctly. This is not redundant with babel — on TL2026,
    `\foreignlanguage` inside the translation leaves no `/Lang` in the
    structure tree at all, so there was previously no way to mark a
    translation whose language differs from the document's, which is the
    normal case when writing in one language and glossing into another.
    Verified with veraPDF on `examples/ua-demo`.
- Fix: `\sublabel` records the label of the level it is used at. It always
  recorded the letter counter, so at the roman level every sub-sub-example
  under one letter stored that same letter, and `\refrange` over them printed
  `(1b-i--b)` instead of `(1b-i--iii)` — a silently wrong reference, since
  nothing about it raised an error. On a main-level example it now records
  nothing and the range closes with a full `\pref` (it used to expand
  `\alph{0}`, i.e. "Counter too large"). `\ref` to a sub-sub-example went
  through a different path and was always correct.
- Fix: an `\altg` in a gloss column with no partner is now a package error
  instead of overlapping text. The two calls of a paradigm are paired by a
  single global toggle (object call sets it, gloss call clears it), so a
  column that announced a stack without completing it left the toggle set
  and gave *every later* `\altg` in that gloss the opposite role — object
  stacks were typeset with the gloss shape, raised half a baseline and
  indented, on top of their neighbours. A `\glll` carrying an `\altg` in a
  third tier hit the same thing, the third call being read as a fresh object
  call. Both compiled silently. Alternatives in three tiers remain
  unsupported; that would be a feature, not a fix.
- The suite can now assert that a case *fails* (`EXPECT_ERROR`), so a package
  error is guarded like any rendering: `tests/altg-unpaired.tex` fails if the
  check stops firing, and the valid `altg` case fails if it starts firing
  when it should not.
- `\lpzgcheck{...}`: consistency checks on the gloss abbreviations
  themselves. Until now the only report was a side effect of building a
  list, so a document that never called `\lpzglist` could mistype
  `\lpzg{pres}` for `\lpzg{prs}` and get PRES in small caps in complete
  silence. `unknown` (**on by default**) reports every abbreviation used
  with no known expansion, at the end of the document, list or no list;
  `ignore={...}` exempts ones deliberately left unexplained. `unused` (off
  by default) reports the reverse — declared with `\SetLeipzig` and never
  used — and considers only your own declarations, not the ~100 built-ins.
  A key a `\lpzglist` has already reported is not reported twice.
- Fix: `\DeclareJudgment` rejects a first mandatory argument that is not a
  single command, instead of hanging. That argument is the command being
  *defined* and the second is the mark it prints — an order easy to read the
  wrong way round, and `\DeclareRobustCommand` takes the first token of
  whatever it is handed, so `\DeclareJudgment{\%\%}{\%\%}` quietly
  redefined `\%`, which the judgment scanner peeks for, and the run then
  spun forever with nothing in the log to say why.
- Test coverage for the previously untested public surface: judgment
  customisation including the `/Alt` it produces under tagging
  (`tests/judgments.tex`), its misuse error (`tests/judgment-badarg.tex`),
  and the numbering parameters at non-default values together with every
  relative-reference command no other case called — `\NNext`, `\TextNext`,
  `\Refrange` and the four `\p*` twins (`tests/customise.tex`). All were
  correct; this is regression insurance, not repair. `tests/lpzgsetup.tex`
  adds the document-wide half of the abbreviation-list interface —
  `\lpzglistsetup`, and `\lpzglisttitle`/`\lpzglistentry` redefined
  wholesale — including that a per-list key beats a document-wide one
  without disturbing the rest of it. Still uncovered: the `\AltBrace*`,
  `\AltgColSep` and `\AltgTransFont` visual tunables.
- The suite caps each engine run (`CASE_TIMEOUT`). A TeX loop ignores
  `-interaction=nonstopmode` and spins with an empty log, so one bad case
  hung the whole suite rather than failing it.
- `\cref` works on examples when `cleveref` is loaded. It used to print
  `?? (1)` — cleveref's marker for a counter it has no name for. The names
  are declared *empty*, so `\cref` prints the number alone, `(1)` and
  `(1a)`, which is how examples are referred to in prose; what it adds over
  `\ref` is cleveref's list and range handling, `\cref{a,b}` giving
  `(1) and (2)`. A `\crefname` the document sets itself is left alone —
  linguexx declares its defaults at `\begin{document}`, after the preamble,
  so without that guard it would silently overwrite the author's choice.
- The manual documents the transliteration extraction trap (§9.3) and is now
  built with **lualatex**, refusing to build under pdflatex. It contains the
  affected characters in the table that explains them, so built with pdflatex
  it exhibited the defect it was describing — copying the first column out of
  the PDF gave you the second. Verified while writing it: tagging does *not*
  repair this (the mapping is per glyph, and there are two glyphs where one
  was meant), and veraPDF passes such a file on all three profiles — so the
  only authoritative PDF/UA check gives a clean bill to a document a screen
  reader misreads.
- `examples/ua-demo.tex` loads `fontenc` (T1 under pdfLaTeX only) and now
  contains an accented example written as literal UTF-8, so the
  accessibility demo exercises the input path the `ç` bug lived on — it
  previously had no non-ASCII character in it at all. Recorded in the file
  itself, because it is easy to assume otherwise: the text layer is correct
  *without* `fontenc` too, since the tagging machinery supplies the
  `ToUnicode` mapping, and veraPDF passes either way. T1 is there as correct
  practice for accented input, not as a fix for a demonstrated defect.
- Fix: **linguexx could not be used with French `babel` at all.** `babel`'s
  French option defines `\fg`, the closing guillemet of `\og … \fg`, and
  linguexx claimed the same name for its glossed shorthand `\fg.` — fatally,
  in both load orders. With linguexx first, `babel`'s own `\newcommand\fg`
  raised "Command `\fg` already defined" and the document did not build.
  With `babel` first, linguexx silently overwrote it and **every closing
  guillemet in the document disappeared**, with no error at all.
  The dot shorthands (`\z.`, `\exg.`, `\ag.`–`\fg.`) are now claimed at
  `\begin{document}` and only if the name is still free. Both halves are
  needed and fix different orders: deferring lets `babel` define `\fg`
  first, and the guard stops linguexx overwriting it afterwards. In a French
  document `\fg.` is therefore unavailable — write `\f.` then `\gll` — and
  the `.log` records why. `\f.` itself is unaffected; `\fg` and `\f` are
  different control sequences. The same guard hands back any shorthand name
  you have defined yourself, `\eg` being the common case.
- The manual's "Notes and limitations" no longer claims the `\b`, `\c`, `\d`
  accents are unavailable — that stopped being true when the letters were
  made to dispatch on a following period, and the note was left promising
  the old broken behaviour.
- Regression coverage for the two `babel` hazards, in both load orders
  (`tests/babel-fr.tex`, `tests/babel-fr-order.tex`, `tests/babel-de.tex`):
  French makes `?` `!` `:` `;` **active**, and `?` is a judgment mark — that
  works only because the scanner uses `\peek_charcode`, which ignores
  catcode, so switching it to `\peek_meaning` would look like a tidy-up and
  silently stop judgments working in French. German's active `"` shorthand
  is checked through the body collector, the gloss tiers and `\altn`. Both
  orders are needed: `\AtBeginDocument` hooks run in registration order, so
  with linguexx loaded first `babel` re-establishes `\fg` afterwards and
  masks a clobber that sticks in the other order.
- Regression coverage for literal UTF-8 input, which is how the `ç` bug
  escaped: every case wrote its accents the way the manual does (`\c c`,
  `\"a`), and none typed what a user types. `tests/utf8.tex` puts raw UTF-8
  in every example position — body collector, judgment scanner, gloss tiers,
  `\glt`, `\altn`/`\altg`, `\exsource` — under all three engines, with the
  languages chosen for the accent command each exercises rather than for
  prestige. Restoring the old letter-clobbering makes it fail under pdflatex
  with the original error while xe/lua still pass, i.e. it reproduces the
  bug's exact signature. No new fault was found: raw multi-byte input
  survives every position already.
- `tests/utf8-unicode.tex` covers what pdflatex cannot represent at all —
  Hittite `ḫ`, Semitic `ʾ ʿ ḏ ṯ ẓ`, Vietnamese stacked vowels, IPA — under
  the Unicode engines only, via a new `ENGINES_FOR` in the suite. Recorded
  there too, because it is silent and matters for accessibility: pdflatex
  *typesets* Indic dot-below (`ḍ ṇ ṭ ṣ ḥ`) and Latvian comma-below
  (`ģ ķ ļ ņ`) correctly but does not *extract* them — copy-paste and screen
  readers get `kr.s.n.ah.` and `gimen , u`. A Unicode engine is needed for
  those scripts to be accessible, not merely to look right.
- The suite now runs veraPDF itself, on a new PDF/UA-2 case (`tests/ua.tex`),
  so the release gate that used to be a manual step before delivering is
  enforced on every run and under all three engines. This is what catches a
  structure element opened at the *wrong moment* — marked content straddling
  its parent, which veraPDF rejects (`<Span> contains <P>`) and which no
  geometric or flat-structure assertion can see. Two supporting changes:
  cases can now declare how many LaTeX passes they need (`PASSES`), because
  PDF/UA validity does not converge until the third run under pdflatex and
  xelatex, and an unconverged file fails veraPDF exactly like a real
  regression would; and the integrity check also rejects a stale `PASSES`
  key. veraPDF is now a hard requirement of the suite.
- The suite gained a structure-*nesting* check (`struct_label_depths`).
  Every flat structure assertion, and veraPDF itself, accepts an inline
  element that is opened and never closed: it stays spec-valid while
  silently reparenting the rest of the document underneath itself. Top-level
  example numbers are siblings, so they must all sit at one depth in the
  tree; if anything leaks, the ones after it drop a level and the check
  fires.
- Regression coverage for both of the above (`tests/cedilla.tex`,
  `tests/cedilla-gb4e.tex`), including the hyperref-title cases and the
  accent-restoration points after each example syntax; mutation-checked.
  Verified with veraPDF on `examples/ua-demo`.

## 1.0
- `\alt`/`\lxAlt` renamed to `\altn`/`\lxAltn`. `\alt` is claimed by
  `beamer`, `glossaries-extra`, `revtex`/`revsymb`, `tex4ht`,
  `mdwtools/syntax`, and several document classes; `\altn` is unclaimed by
  anything in TeX Live 2026. `\altg`/`\lxAltg` are unaffected -- no known
  collisions, so the name stays. As before, if some other package already
  owns `\altn`, `linguexx` leaves it alone and only `\lxAltn` is available.
  **Breaking change**: documents using `\alt` must switch to `\altn`.

## 0.14
- `\altg`/`\lxAltg` return, rebuilt in text mode as a glossed paradigm of
  alternatives. Inside an interlinear gloss it is written **twice** -- once in
  the object line with the object words, once in the gloss line with their
  glosses:

  ```latex
  \exg. Die \altg{Frau}{Socke}{Maus}{Tonne} ist da.\\
        The.\lpzg{sg} \altg{woman.\lpzg{sg}}{sock.\lpzg{sg}}%
          {mouse.\lpzg{sg}}{ton.\lpzg{sg}} is.\lpzg{prs} there.\\
  ```

  The two calls occupy the two tiers of one gloss column and assemble a
  single paradigm: object stack on the left, gloss stack to its right
  (offset by `\AltgColSep`), braced on BOTH sides, centred on the
  object/gloss midline -- with four alternatives, rows 2 and 3 ride the
  object and gloss lines and rows 1 and 4 protrude symmetrically, with
  surrounding lines kept clear. The example number stays on the object
  baseline. Both calls must list the same number of alternatives (package
  error otherwise), and no spaces may separate the brace groups (break long
  calls with `%`). Outside a gloss, a single `\altg` sets one both-braced
  stack on the current baseline. Under tagging each call is wrapped in a
  `Span` carrying its own spoken `/Alt` ("Frau, Socke, Maus, or Tonne" /
  "woman.singular, ..."); simple `\lpzg` keys are expanded from the Leipzig
  table in the spoken form, compound or unknown keys pass through verbatim,
  and inside the printed stacks `\lpzg` reduces to plain small caps. No math
  anywhere, so the PDF/UA-2 `Formula`-in-`Span` failure that forced the 0.12
  removal cannot recur. Settings: `\AltgColSep` (default `1.2em`),
  `\AltgTransFont` (gloss stack font, default `\normalfont`); the braces
  share `\AltBraceWidth`/`\AltBraceAmplitude`/`\AltBraceSep` with `\alt`.
  Under beamer or any class that owns `\altg`, use `\lxAltg`.
- `\alt` closes its stack with a right brace again, restoring the pre-0.13
  math-mode look (`\left\{ ... \right\}`); v0.13 drew the left brace only.
- Brace direction fixed: the TikZ `brace` decoration bulges according to the
  path direction, and the v0.13 path drew the brace mirrored (a closing
  shape on the left of the stack). Both `\alt` and `\altg` braces now curve
  the right way.

## 0.13
- `\alt` rebuilt in text mode: a `tabular` stack with a TikZ-drawn brace, no
  math and no `amsmath`. The alternatives are now ordinary tagged text, and
  under tagging the stack is wrapped in a `Span` carrying a spoken `/Alt`
  ("A, B, or C", built with `\text_purify:n` so formatting is stripped for
  speech). Brace tunables: `\AltBraceWidth`, `\AltBraceAmplitude`,
  `\AltBraceSep`, `\AltBraceRaise`. Dependency change: `-amsmath`, `+graphicx`,
  `+tikz`.

## 0.12
- Remove `\altg`/`\lxAltg` (alternatives with translations) and its
  `\AltgColSep`/`\AltgTransFont` settings. `\alt` is unaffected.

## 0.11
- Revert the 0.10 `\alt`/`\altg` tagging. Wrapping the alternatives formula in a
  `Span` carrying `/Alt` is invalid under PDF/UA-2 when the formula begins an
  example (a `Span` may not contain the `Part`/`P` the math tagging then builds),
  and veraPDF rejects it. `\alt`/`\altg` revert to the plain formula, which
  validates; a spoken form needs the "positioning text" math interface and is
  deferred.

## 0.10 (reverted in 0.11)
- `\alt` and `\altg` (stacked alternatives, set as math arrays) now carry a
  spoken alternate text under tagging -- "alternatively: the cat, a dog, or the
  bird" -- so a screen reader no longer reads the braces and array as a jumble;
  `\altg` keeps each translation with its alternative.

## 0.9
- `\lpzg` accepts a whole compound gloss label in one call: `\lpzg{3sg.pst}`
  splits on periods, peels a leading person digit, expands each piece and joins
  them into one `/E` ("third person singular past").
- `\GlossTierLang` is now scoped: a document-wide default when set in the
  preamble or body, overridable for a single example by issuing it inside that
  example (it reverts afterwards).

## 0.8
- PDF tagging, objective 6: Leipzig gloss abbreviations. `\lpzg{sg}` sets the
  abbreviation in small caps and, under tagging, records its expansion as the
  PDF `/E` (expansion text), so a screen reader announces "singular" while print
  and copy-paste keep SG. Built-in standard Leipzig table (keyed by short form);
  `\SetLeipzig{key}{expansion}` extends or overrides; unknown keys print with no
  expansion. Self-contained (no dependency on the `leipzig` package).

## 0.7
- PDF tagging, objective 5: language of a gloss tier. `\GlossTierLang{tier}{code}`
  records a language code; under tagging each word of that tier is wrapped in a
  span carrying `/Lang`, so the object language is pronounced with its own
  phonetics.

## 0.6
- PDF tagging, objective 4: interlinear glosses as structure. Each gloss column
  (an object word with its aligned glosses) is grouped as a span, so a screen
  reader reads the gloss word-bundle by word-bundle in the correct order rather
  than as loose text.

## 0.5
- Fix an invalid PDF attribute value introduced in 0.3: ordered example lists
  emitted `/ListNumbering /Ordered`, which is not a spec-valid value and is
  rejected by validators. Each level now uses a valid class of its own
  (`/Decimal`, `/LowerAlpha`, `/LowerRoman`), with a safe fallback to `/None`.

## 0.4
- PDF tagging, objective 3: spoken forms for judgment marks. Under tagging a
  mark is wrapped in a span carrying `/Alt`, so it is announced by meaning
  ("ungrammatical") rather than by glyph. Defaults for `* ? ?? ?* # %`;
  `\DeclareJudgment[spoken=…]` and `\SetJudgmentSpoken` to customise.

## 0.3
- PDF tagging, objective 2: examples as proper list structure. Examples already
  produced `L → LI → Lbl → LBody` with nested sub-levels (they are real lists);
  this marked them as ordered. (The value used here was corrected in 0.5.)

## 0.2
- PDF tagging, objective 1: tagging-safety under `\DocumentMetadata`. Valid
  structure tree on all three engines, including examples inside footnotes.
  Lists opened/closed through the environment interface; no extra group around
  the example; explicit paragraph-continuation and text-unit cleanup.
- Label alignment under tagging: labels are set flush left in a full-width box so
  the tagged list code does not re-box them flush right.

## 0.1
- `[legacy]` option reproducing `linguex` geometry and conventions exactly;
  orthogonal to the syntax options, so combinable (e.g. `[legacy,gb4e]`).
- Default ("lazy") mode: empty `\firstrefdash` (references print "(3a)", not
  "(3-a)"); `\resetExdefaults` restores the current mode's lengths.

## Earlier
- Evolution from `linguex-patch.sty` into a standalone `expl3`-based package,
  removing dependencies on `linguex` / `cgloss4e` / `xspace`; complete manual and
  regression suite across pdfLaTeX, XeLaTeX and LuaLaTeX.
