% Input:       the complete record set, with the policy wires derived.
% Output:      resolved places, hulls, routes, lanes, and the equation's shared measure.
% Owned state: the address dependency graph and the resolved-place table.
% Invariants:  resolution reads records and metrics only; a cycle is a coded error.
% Next stage:  tenkz-kernel-ink lays the resolved geometry down.
% SPDX-License-Identifier: Apache-2.0
% Copyright the TNLean project; see LICENSE for the full terms.
%
% tenkz-kernel-resolve.code.tex is the resolution stage: placement, plane-ratio guards, the address
%   dependency graph, points on a curve, the selector and its offset hull,
%   the equation's shared measure, cell sets, hull routes, and leg lanes.
%
% Split out of tenkz-kernel.code.tex without changing a line of content;
% the stages load in the order tenkz-kernel.code.tex lists them, and the
% golden event and pixel streams are unchanged by the move.

% Load guard.  It stands before \ExplSyntaxOn deliberately: a guarded exit
% never reaches the trailing \ExplSyntaxOff, so a guard inside expl3 syntax
% would leave the caller's catcodes changed -- ignoring spaces rather than
% erroring.  Guarding first leaves them exactly as they were found, whether
% the caller had expl3 syntax on or off.
\ifcsname tenkzkernel\endcsname \expandafter\endinput \fi

\ExplSyntaxOn

%
% Ink order is the stage invariant, refined for strings: plain index wires;
% physical legs (crossing-referenced legs are saved, not yet drawn); strings
% (declare, crossings, surgery, then draw legs and strings from their saved
% paths); atoms; marks.  A string's gap is cut before any of its ink exists.
% One route family stands outside that surgery: a physical closure's `trace`
% stroke carries its own paper halo instead of a crossing-derived gap, so it
% must still be the last ink at any crossing it makes.  Among saved index
% routes it therefore draws after the deferred legs it may cross, not before
% them, while every other index route keeps the ordinary first-pass order.

\msg_new:nnn {tenkz}{kernel-render-todo}
  { [TKZ-KERNEL-RENDER-TODO]~'#1'~does~not~draw~yet~(record~#2) }
\msg_new:nnn {tenkz}{kernel-skin}
  { [TKZ-KERNEL-SKIN]~no~skin~named~'#1'~is~primitive~or~declared }
\msg_new:nnn {tenkz}{kernel-render-unplaced}
  { [TKZ-KERNEL-RENDER-UNPLACED]~record~'#1'~has~no~address,~node,~or~
    cluster~parent~to~resolve }
\msg_new:nnn {tenkz}{kernel-render-crossref}
  { [TKZ-KERNEL-RENDER-CROSSREF]~crossing~operand~'#1'~names~no~string~or~
    leg~of~this~picture }
\msg_new:nnn {tenkz}{kernel-render-onwire}
  { [TKZ-KERNEL-RENDER-ONWIRE]~address~'#1'~names~no~wire~of~this~picture }
\msg_new:nnn {tenkz}{kernel-render-phtrace}
  { [TKZ-KERNEL-RENDER-PHTRACE]~physical~trace~record~'#1'~has~no~rendered~
    atom~at~row~1,~column~#2 }
\msg_new:nnn {tenkz}{kernel-skin-slot}
  { [TKZ-SKIN-PAIRING-SLOT]~slot~#1~on~face~#2~of~#3~exceeds~its~span~#4 }

% variants this pass leans on; generation is idempotent where l3 already
% ships the form
\cs_generate_variant:Nn \str_case:nn { Vn, en }
\cs_generate_variant:Nn \str_case:nnF { VnF }
\cs_generate_variant:Nn \prop_get:NnNF { NVNF }
\cs_generate_variant:Nn \clist_map_inline:nn { Vn }

\cs_new:Npn \__tenkz_kernel_r_item:nn #1#2
  { \prop_item:Ne \l__tenkz_kernel_node_prop { #1 / #2 } }

\prop_new:N \l__tenkz_kernel_r_placed_prop    % placement ids already requested
\prop_new:N \l__tenkz_kernel_r_legref_prop    % leg path id -> referencing wire id
\prop_new:N \l__tenkz_kernel_r_legdrawn_prop  % leg path id -> deferred (saved)
\bool_new:N \l__tenkz_kernel_topology_bool
\prop_new:N \l__tenkz_kernel_r_species_prop   % species word -> hue
\prop_new:N \l__tenkz_kernel_r_sid_record_prop
\prop_new:N \l__tenkz_kernel_r_index_style_prop
\prop_new:N \l__tenkz_kernel_r_index_sid_prop
% wire id -> direction-mark station fraction (fp decimal text), stashed
% where the style is composed so the ink pass can cover the barb without
% replaying that style a second time.
\prop_new:N \l__tenkz_kernel_r_dir_mark_prop
\prop_new:N \l__tenkz_kernel_r_skin_over_wire_prop
\prop_new:N \l__tenkz_kernel_r_port_box_prop
\prop_new:N \l__tenkz_kernel_r_port_host_routes_prop
\clist_new:N \l__tenkz_kernel_r_skin_over_wire_clist
\seq_new:N  \l__tenkz_kernel_r_index_routes_seq
% A saved-route physical closure crosses a deferred leg's ink, not the other
% way round: sieved out of the ordinary index-route pass and drawn again
% after the deferred legs, so its `trace` paper halo remains the top ink.
\seq_new:N  \l__tenkz_kernel_r_deferred_trace_routes_seq
\seq_new:N  \l__tenkz_kernel_r_port_label_routes_seq
\seq_new:N  \l__tenkz_kernel_r_strings_seq    % string wire record ids
\seq_new:N  \l__tenkz_kernel_r_after_atom_seq % physical traces need glyph borders
\int_new:N  \l__tenkz_kernel_r_rows_int
\int_new:N  \l__tenkz_kernel_r_cols_int
\int_new:N  \l__tenkz_kernel_r_species_cycle_int
\tl_new:N   \l__tenkz_kernel_r_tl
\tl_new:N   \l__tenkz_kernel_r_b_tl
\tl_new:N   \l__tenkz_kernel_r_c_tl
\tl_new:N   \l__tenkz_kernel_r_row_tl
\tl_new:N   \l__tenkz_kernel_r_col_tl
\tl_new:N   \l__tenkz_kernel_r_pts_tl
\tl_new:N   \l__tenkz_kernel_r_sid_tl
\tl_new:N   \l__tenkz_kernel_r_wire_record_tl
\tl_new:N   \l__tenkz_kernel_r_touch_a_tl
\tl_new:N   \l__tenkz_kernel_r_touch_b_tl
\tl_new:N   \l__tenkz_kernel_r_touch_c_tl
\tl_new:N   \l__tenkz_kernel_r_touch_d_tl
\tl_new:N   \l__tenkz_kernel_r_touch_row_tl
\tl_new:N   \l__tenkz_kernel_r_touch_col_tl
\bool_new:N \l__tenkz_kernel_r_touch_cell_bool
\tl_new:N   \l__tenkz_kernel_r_hue_tl
\tl_new:N   \l__tenkz_kernel_r_species_tl
\tl_new:N   \l__tenkz_kernel_r_host_tl
\tl_new:N   \l__tenkz_kernel_r_host_species_tl
\tl_new:N   \l__tenkz_kernel_r_species_descriptor_tl
\tl_new:N   \l__tenkz_kernel_r_species_declared_hue_tl
\tl_new:N   \l__tenkz_kernel_r_mark_side_tl
\bool_new:N \l__tenkz_kernel_r_mark_side_default_bool
\bool_new:N \l__tenkz_kernel_r_mark_text_bool
\tl_new:N   \l__tenkz_kernel_r_mark_form_tl
\tl_new:N   \l__tenkz_kernel_r_mark_ink_tl
\tl_new:N   \l__tenkz_kernel_r_mark_style_tl
\tl_new:N   \l__tenkz_kernel_r_wire_ink_tl
\tl_new:N   \l__tenkz_kernel_r_wire_style_tl
\tl_new:N   \l__tenkz_kernel_r_atom_style_tl
\seq_new:N  \l__tenkz_kernel_r_hue_match_seq
\tl_new:N   \l__tenkz_kernel_r_x_tl
\tl_new:N   \l__tenkz_kernel_r_y_tl
\tl_new:N   \l__tenkz_kernel_r_xb_tl
\tl_new:N   \l__tenkz_kernel_r_yb_tl
% call-site registers the node resolver never touches: a node id must
% survive its own resolution
\tl_new:N   \l__tenkz_kernel_r_node_tl
\tl_new:N   \l__tenkz_kernel_r_end_tl
\tl_new:N   \l__tenkz_kernel_r_from_tl
\tl_new:N   \l__tenkz_kernel_r_to_tl
\seq_new:N  \l__tenkz_kernel_r_scan_seq
\fp_new:N   \l__tenkz_kernel_r_x_fp
\fp_new:N   \l__tenkz_kernel_r_y_fp
\fp_new:N   \l__tenkz_kernel_r_xb_fp
\fp_new:N   \l__tenkz_kernel_r_yb_fp
\fp_new:N   \l__tenkz_kernel_r_xc_fp
\fp_new:N   \l__tenkz_kernel_r_yc_fp
\fp_new:N   \l__tenkz_kernel_r_reach_fp
\fp_new:N   \l__tenkz_kernel_r_port_x_fp
\fp_new:N   \l__tenkz_kernel_r_port_y_fp
\fp_new:N   \l__tenkz_kernel_r_port_tip_x_fp
\fp_new:N   \l__tenkz_kernel_r_port_tip_y_fp
\fp_new:N   \l__tenkz_kernel_r_port_reach_fp
\tl_new:N   \l__tenkz_kernel_r_port_turn_tl
\bool_new:N \l__tenkz_kernel_r_from_bool
\bool_new:N \l__tenkz_kernel_r_to_bool
\dim_new:N  \l__tenkz_kernel_r_north_x_dim
\dim_new:N  \l__tenkz_kernel_r_north_y_dim
\dim_new:N  \l__tenkz_kernel_r_south_y_dim
\dim_new:N  \l__tenkz_kernel_r_east_x_dim
% the arc route's control frame: chord bearing, control distance, one end's
% bearing and side while it is read, the four control coordinates, and the
% finished cubic
\fp_new:N   \l__tenkz_kernel_r_arc_a_fp
\fp_new:N   \l__tenkz_kernel_r_arc_d_fp
\fp_new:N   \l__tenkz_kernel_r_arc_t_fp
\fp_new:N   \l__tenkz_kernel_r_arc_s_fp
\fp_new:N   \l__tenkz_kernel_r_arc_tf_fp
\fp_new:N   \l__tenkz_kernel_r_arc_af_fp
\fp_new:N   \l__tenkz_kernel_r_arc_ab_fp
\fp_new:N   \l__tenkz_kernel_r_arc_cax_fp
\fp_new:N   \l__tenkz_kernel_r_arc_cay_fp
\fp_new:N   \l__tenkz_kernel_r_arc_cbx_fp
\fp_new:N   \l__tenkz_kernel_r_arc_cby_fp
\fp_new:N   \l__tenkz_kernel_r_arc_px_fp
\fp_new:N   \l__tenkz_kernel_r_arc_py_fp
\fp_new:N   \l__tenkz_kernel_r_arc_delta_fp
\fp_new:N   \l__tenkz_kernel_r_arc_mx_fp
\fp_new:N   \l__tenkz_kernel_r_arc_my_fp
\fp_new:N   \l__tenkz_kernel_r_arc_nx_fp
\fp_new:N   \l__tenkz_kernel_r_arc_ny_fp
\bool_new:N \l__tenkz_kernel_r_arc_wrap_bool
% the wrap resolver's saved far end, fold-two control frame, and the two
% stretch lengths
\fp_new:N   \l__tenkz_kernel_r_arc_ex_fp
\fp_new:N   \l__tenkz_kernel_r_arc_ey_fp
\fp_new:N   \l__tenkz_kernel_r_arc_gax_fp
\fp_new:N   \l__tenkz_kernel_r_arc_gay_fp
\fp_new:N   \l__tenkz_kernel_r_arc_gbx_fp
\fp_new:N   \l__tenkz_kernel_r_arc_gby_fp
\fp_new:N   \l__tenkz_kernel_r_arc_lone_fp
\fp_new:N   \l__tenkz_kernel_r_arc_ltwo_fp
\fp_new:N   \l__tenkz_kernel_r_arc_lthree_fp
\fp_new:N   \l__tenkz_kernel_r_arc_h_fp
\fp_new:N   \l__tenkz_kernel_r_arc_side_fp
\fp_new:N   \l__tenkz_kernel_r_arc_done_fp
\fp_new:N   \l__tenkz_kernel_r_arc_dtwo_fp
\fp_new:N   \l__tenkz_kernel_r_arc_hx_fp
\fp_new:N   \l__tenkz_kernel_r_arc_hy_fp
\fp_new:N   \l__tenkz_kernel_r_arc_len_fp
\fp_new:N   \l__tenkz_kernel_r_arc_tgt_fp
\seq_new:N  \l__tenkz_kernel_r_arc_cum_seq
\tl_new:N   \l__tenkz_kernel_r_arc_tl
\tl_new:N   \l__tenkz_kernel_r_arc_host_tl
\tl_new:N   \l__tenkz_kernel_r_arc_face_tl
\tl_new:N   \l__tenkz_kernel_r_arc_path_tl

% one conversion: a pitch-unit fp expression as a pt spelling
\cs_new:Npn \__tenkz_kernel_r_pt:n #1
  {
    \fp_to_dim:n
      { (#1) * \dim_to_fp:n { \use:c {tenkz@pitch} } }
  }
\cs_new:Npn \__tenkz_kernel_r_pair:nn #1#2
  { ( \__tenkz_kernel_r_pt:n {#1} , \__tenkz_kernel_r_pt:n {#2} ) }
% engine points are braced caller-space pairs
\cs_new:Npn \__tenkz_kernel_r_ppt:nn #1#2
  { { \__tenkz_kernel_r_pt:n {#1} , \__tenkz_kernel_r_pt:n {#2} } }

\cs_new:Npn \__tenkz_kernel_r_compass:n #1
  {
    % the argument may arrive as an unexpanded register: compare its string
    \str_case:enF {#1}
      { {n}{90} {ne}{45} {e}{0} {se}{315} {s}{270} {sw}{225} {w}{180} {nw}{135} }
      {#1}
  }

\cs_new_protected:Npn \__tenkz_kernel_r_xy:nNN #1#2#3
  {
    % the geometry store is n-typed: the id expands here, once
    \exp_args:Ne \__tenkz_geom_xy:nNN {#1}
      \l__tenkz_kernel_r_x_tl \l__tenkz_kernel_r_y_tl
    \fp_set:Nn #2 { \l__tenkz_kernel_r_x_tl + 0 }
    \fp_set:Nn #3 { \l__tenkz_kernel_r_y_tl + 0 }
  }

% "( r , c )" -> the two integers
\cs_new_protected:Npn \__tenkz_kernel_r_rc:nNN #1#2#3
  {
    \regex_extract_once:nnN { (\d+) \D+ (\d+) } {#1} \l__tenkz_kernel_match_seq
    \tl_set:Ne #2 { \seq_item:Nn \l__tenkz_kernel_match_seq {2} }
    \tl_set:Ne #3 { \seq_item:Nn \l__tenkz_kernel_match_seq {3} }
  }

\cs_new_protected:Npn \__tenkz_kernel_basis_record_maybe:nnnN #1#2#3#4
  {
    \int_compare:nNnTF { \l__tenkz_kernel_basis_count_int } = {0}
      {
        \int_compare:nNnTF {#3} = {1}
          { \__tenkz_kernel_select_cell:nnN {#1}{#2} #4 }
          { \tl_set:Nn #4 { \q_no_value } }
      }
      { \prop_get:NnN \l__tenkz_kernel_basis_cell_prop {#1-#2-#3} #4 }
  }
\cs_new_protected:Npn \__tenkz_kernel_basis_record:nnnN #1#2#3#4
  {
    \__tenkz_kernel_basis_record_maybe:nnnN {#1}{#2}{#3} #4
    \quark_if_no_value:NT #4
      { \msg_error:nnnnn {tenkz}{kernel-frame-member-range} {#1}{#2}{#3} }
  }
\cs_generate_variant:Nn \__tenkz_kernel_basis_record_maybe:nnnN { eeeN }
\cs_generate_variant:Nn \__tenkz_kernel_basis_record:nnnN { eeeN }

% The grid cell an atom occupies, whichever way the author placed it: the
% body's implicit cell arrives as address TEXT, an "at=" or named placement
% as an address NODE.  Ports and physical legs are offsets from that cell,
% so both spellings have to answer the same question.  A non-cell placement
% (relative, midway, on a wire) has no row and column: the caller is told so
% by the boolean and keeps its own counsel.
\cs_new_protected:Npn \__tenkz_kernel_atom_rc:nNNN #1#2#3#4
  {
    \bool_set_true:N #4
    \__tenkz_model_get:nnN {#1} {addr} \l__tenkz_kernel_r_rc_tl
    \quark_if_no_value:NTF \l__tenkz_kernel_r_rc_tl
      {
        \__tenkz_model_get:nnN {#1} {node} \l__tenkz_kernel_r_rc_tl
        \bool_lazy_and:nnTF
          { ! \quark_if_no_value_p:N \l__tenkz_kernel_r_rc_tl }
          {
            \str_if_eq_p:ee
              { \__tenkz_kernel_node_item:Nn \l__tenkz_kernel_r_rc_tl {kind} }
              {cell}
          }
          {
            \tl_set:Ne #2
              { \__tenkz_kernel_node_item:Nn \l__tenkz_kernel_r_rc_tl {row} }
            \tl_set:Ne #3
              { \__tenkz_kernel_node_item:Nn \l__tenkz_kernel_r_rc_tl {col} }
          }
          { \bool_set_false:N #4 }
      }
      { \exp_args:NV \__tenkz_kernel_r_rc:nNN \l__tenkz_kernel_r_rc_tl #2 #3 }
  }
\tl_new:N \l__tenkz_kernel_r_rc_tl

% span extents of an atom record: wide and wires with default 1
\cs_new_protected:Npn \__tenkz_kernel_r_span:nNN #1#2#3
  {
    \__tenkz_model_get:nnN {#1} {wide} #2
    \quark_if_no_value:NT #2 { \tl_set:Nn #2 {1} }
    \__tenkz_model_get:nnN {#1} {wires} #3
    \quark_if_no_value:NT #3 { \tl_set:Nn #3 {1} }
  }

% ---------- placement resolution --------------------------------------------------------------
% The picture frame: logical (row, col) to page (x, y).  Flat is the default
% and has rows downward, columns rightward, and cell (1,1) at the origin.
% On a circle a column is a station and a row is a step toward the centre;
% the stations stand one pitch apart, which fixes the radius the contract
% left unstated, and the first of them stands at the top, where its tangent
% is the flat frame's own eastward axis.  The clause is the circle's alone:
% a projected frame foreshortens its receding row by construction.
\cs_new:Npn \__tenkz_kernel_r_frame_word:
  {
    \prop_if_in:NnTF \l__tenkz_kernel_policy_prop {frame}
      { \prop_item:Nn \l__tenkz_kernel_policy_prop {frame} }
      { flat }
  }
\cs_new_protected:Npn \__tenkz_kernel_r_frame:
  {
    \str_case_e:nn { \__tenkz_kernel_r_frame_word: }
      {
        {circle}
          {
            \__tenkz_geom_frame_declare_circle:nnnn {kpic}
              { \__tenkz_kernel_cols: }
              {
                \__tenkz_kernel_cols: < 2
                  ? 0 : 1 / ( 2 * sind( 180 / \__tenkz_kernel_cols: ) )
              }
              {90}
          }
        {plane}
          { \__tenkz_geom_frame_declare_plane:n {kpic} }
        {flat}
          {
            \__tenkz_geom_frame_declare_affine:nnnnnnn
              {kpic} {0}{1}{-1}{0} {-1}{1}
          }
      }
  }

% ---------- plane-ratio guards ----------
% Warnings, never clamps: the ratios draw what they are told, and the log
% names the regime the value has entered.  Thresholds come from named metrics
% or dimensionless geometry, so a metric retune moves them automatically;
% measured examples only explain failure modes.
\msg_new:nnn { tenkz } { plane-rise-low }
  { plane~rise=#1~is~at~or~below~the~normalized~physical-leg~clearance~
    threshold~#2:~vertical~
    physical~legs~cross~the~behind~row's~in-plane~bond~(false~junctions;~
    the~worst~sweep~cell~0.30/0.40~produced~false~5-valent~junctions).~
    Keep~plane~rise~above~#2. }
\msg_new:nnn { tenkz } { plane-slant-band }
  { plane~slant=#1~is~outside~the~readable~band~(#2,~#3).~Choose~a~
    plane~slant~strictly~inside~this~band.~Below~the~band,~depth~bonds~
    trend~toward~vertical~physical~legs;~above~the~band,~depth~neighbours~
    trend~toward~column~neighbours. }
\msg_new:nnn { tenkz } { plane-tall-window }
  { (rows-1)~x~plane~slant~=~#1~exceeds~(cols-1)/2~=~#2.~The~sheared~
    projection~approaches~a~rhombus~staircase~and~can~be~mistaken~for~
    a~rotated~lattice~(plane_sweep1~page~2b;~5x3~drift/width~0.9).~
    Reduce~the~row~count,~increase~the~column~count,~or~lower~the~plane~
    slant~while~keeping~it~above~the~readable~floor~#3. }

% A projected picture carries its map as model data.  This is recorded after
% authored and derived topology, so enabling a frame cannot renumber atoms or
% wires.  The readability guard thresholds are evaluated from named metrics:
% a future metric retune therefore reports the same hazards as the lattice
% sweep rather than silently changing the projection.
\cs_new_protected:Npn \__tenkz_kernel_plane_model:
  {
    \str_if_eq:eeT { \__tenkz_kernel_r_frame_word: } {plane}
      {
        \__tenkz_model_record_frame:e
          {
            map = plane , scope = picture ,
            a = -\__tenkz_metric_ratio:n {planeslant} , b = 1 ,
            c = -\__tenkz_metric_ratio:n {planerise} , d = 0 ,
            dx = \fp_eval:n { \__tenkz_metric_ratio:n {planeslant} - 1 } ,
            dy = \__tenkz_metric_ratio:n {planerise} ,
            transverse-x =
              \fp_use:N \c__tenkz_geom_plane_transverse_x_fp ,
            transverse-y =
              \fp_use:N \c__tenkz_geom_plane_transverse_y_fp
          }
        \fp_compare:nNnF
          { \__tenkz_metric_ratio:n {planerise} }
          > { \__tenkz_metric_ratio:n {physleg} }
          {
            \msg_warning:nnee {tenkz}{plane-rise-low}
              { \__tenkz_metric_ratio:n {planerise} }
              { \__tenkz_metric_ratio:n {physleg} }
          }
        \bool_lazy_and:nnF
          {
            \fp_compare_p:nNn
              { \__tenkz_metric_ratio:n {planeslant} }
              > { \__tenkz_metric_ratio:n {slantmin} }
          }
          {
            \fp_compare_p:nNn
              { \__tenkz_metric_ratio:n {planeslant} }
              < { \__tenkz_metric_ratio:n {slantmax} }
          }
          {
            \msg_warning:nneee {tenkz}{plane-slant-band}
              { \__tenkz_metric_ratio:n {planeslant} }
              { \__tenkz_metric_ratio:n {slantmin} }
              { \__tenkz_metric_ratio:n {slantmax} }
          }
        \fp_compare:nNnT
          {
            ( \__tenkz_kernel_rows: - 1 )
            * \__tenkz_metric_ratio:n {planeslant}
          }
          > { ( \__tenkz_kernel_cols: - 1 ) / 2 }
          {
            \msg_warning:nneee {tenkz}{plane-tall-window}
              {
                \fp_eval:n
                  {
                    ( \__tenkz_kernel_rows: - 1 )
                    * \__tenkz_metric_ratio:n {planeslant}
                  }
              }
              { \fp_eval:n { ( \__tenkz_kernel_cols: - 1 ) / 2 } }
              { \__tenkz_metric_ratio:n {slantmin} }
          }
      }
  }

% Each declared basis member is one family of translated sites, not an
% unrelated page point.  Geometry finds the smallest conservative centre-bearing
% default-bead support margin over the finite cell window after the complete
% affine map.  Kernel policy supplies only the shared bead-body radius and the
% page-owned full-stroke-plus-daylight gap.  The diagnostic never changes
% model identity, placement, or connectivity.
%
% Population indexes each live record by its canonical member address.  Whole-
% cell overrides contribute no record; member-addressed replacements remain
% live; spanning records contribute only their anchor, including row or column
% zero when a positive span reaches the declared window.  For each member pair
% the plan either enumerates its sparse record product or marks it dense,
% whichever is bounded by the complete [-rows,rows] x [-cols,cols] translation
% window.  Geometry owns the raw support calculation and asks this kernel
% callback about model realization only for negative margins.
\cs_new_protected:Npn \__tenkz_kernel_basis_spacing_sparse_inner:n #1
  {
    \prop_get:NeN \l__tenkz_kernel_basis_live_record_prop
      { \int_use:N \l__tenkz_kernel_basis_spacing_b_int / #1 }
      \l__tenkz_kernel_basis_live_record_tl
    \seq_set_split:NnV \l__tenkz_kernel_basis_spacing_address_seq {/}
      \l__tenkz_kernel_basis_live_record_tl
    \int_set:Nn \l__tenkz_kernel_basis_spacing_b_row_int
      { \seq_item:Nn \l__tenkz_kernel_basis_spacing_address_seq {1} }
    \int_set:Nn \l__tenkz_kernel_basis_spacing_b_col_int
      { \seq_item:Nn \l__tenkz_kernel_basis_spacing_address_seq {2} }
    \tl_set:Ne \l__tenkz_kernel_basis_spacing_b_id_tl
      { \seq_item:Nn \l__tenkz_kernel_basis_spacing_address_seq {3} }
    \str_if_eq:eeF
      { \l__tenkz_kernel_basis_spacing_a_id_tl }
      { \l__tenkz_kernel_basis_spacing_b_id_tl }
      {
        \prop_put:Nee \l__tenkz_kernel_basis_spacing_plan_prop
          {
            candidate /
            \int_use:N \l__tenkz_kernel_basis_spacing_a_int /
            \int_use:N \l__tenkz_kernel_basis_spacing_b_int /
            \int_eval:n
              {
                \l__tenkz_kernel_basis_spacing_b_row_int
                - \l__tenkz_kernel_basis_spacing_a_row_int
              } /
            \int_eval:n
              {
                \l__tenkz_kernel_basis_spacing_b_col_int
                - \l__tenkz_kernel_basis_spacing_a_col_int
              }
          }
          {1}
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_basis_spacing_sparse_outer:n #1
  {
    \prop_get:NeN \l__tenkz_kernel_basis_live_record_prop
      { \int_use:N \l__tenkz_kernel_basis_spacing_a_int / #1 }
      \l__tenkz_kernel_basis_live_record_tl
    \seq_set_split:NnV \l__tenkz_kernel_basis_spacing_address_seq {/}
      \l__tenkz_kernel_basis_live_record_tl
    \int_set:Nn \l__tenkz_kernel_basis_spacing_a_row_int
      { \seq_item:Nn \l__tenkz_kernel_basis_spacing_address_seq {1} }
    \int_set:Nn \l__tenkz_kernel_basis_spacing_a_col_int
      { \seq_item:Nn \l__tenkz_kernel_basis_spacing_address_seq {2} }
    \tl_set:Ne \l__tenkz_kernel_basis_spacing_a_id_tl
      { \seq_item:Nn \l__tenkz_kernel_basis_spacing_address_seq {3} }
    \int_step_function:nnN {1}
      { \l__tenkz_kernel_basis_spacing_b_count_int }
      \__tenkz_kernel_basis_spacing_sparse_inner:n
  }

\cs_new_protected:Npn \__tenkz_kernel_basis_spacing_plan_b:n #1
  {
    \int_set:Nn \l__tenkz_kernel_basis_spacing_b_int {#1}
    \prop_get:NeNTF \l__tenkz_kernel_basis_live_count_prop {#1} \l_tmpa_tl
      {
        \int_set:Nn \l__tenkz_kernel_basis_spacing_b_count_int
          { \l_tmpa_tl }
      }
      { \int_zero:N \l__tenkz_kernel_basis_spacing_b_count_int }
    \int_compare:nNnTF
      {
        \l__tenkz_kernel_basis_spacing_a_count_int
        * \l__tenkz_kernel_basis_spacing_b_count_int
      }
      < { \l__tenkz_kernel_basis_spacing_translation_count_int }
      {
        \int_step_function:nnN {1}
          { \l__tenkz_kernel_basis_spacing_a_count_int }
          \__tenkz_kernel_basis_spacing_sparse_outer:n
      }
      {
        \prop_put:Nee \l__tenkz_kernel_basis_spacing_plan_prop
          {
            dense /
            \int_use:N \l__tenkz_kernel_basis_spacing_a_int /
            \int_use:N \l__tenkz_kernel_basis_spacing_b_int
          }
          {1}
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_basis_spacing_plan_a:n #1
  {
    \int_set:Nn \l__tenkz_kernel_basis_spacing_a_int {#1}
    \prop_get:NeNTF \l__tenkz_kernel_basis_live_count_prop {#1} \l_tmpa_tl
      {
        \int_set:Nn \l__tenkz_kernel_basis_spacing_a_count_int
          { \l_tmpa_tl }
      }
      { \int_zero:N \l__tenkz_kernel_basis_spacing_a_count_int }
    \int_step_function:nnN
      { \l__tenkz_kernel_basis_spacing_a_int + 1 }
      { \l__tenkz_kernel_basis_count_int }
      \__tenkz_kernel_basis_spacing_plan_b:n
  }

\cs_new_protected:Npn \__tenkz_kernel_basis_spacing_candidates:
  {
    \prop_clear:N \l__tenkz_kernel_basis_spacing_east_prop
    \prop_clear:N \l__tenkz_kernel_basis_spacing_north_prop
    \prop_clear:N \l__tenkz_kernel_basis_spacing_plan_prop
    \prop_map_inline:Nn \l__tenkz_kernel_basis_east_prop
      {
        \prop_put:Nee \l__tenkz_kernel_basis_spacing_east_prop {##1}
          { \fp_eval:n { ##2 * \c__tenkz_kernel_basis_step_fp } }
      }
    \prop_map_inline:Nn \l__tenkz_kernel_basis_north_prop
      {
        \prop_put:Nee \l__tenkz_kernel_basis_spacing_north_prop {##1}
          { \fp_eval:n { ##2 * \c__tenkz_kernel_basis_step_fp } }
      }
    \int_set:Nn \l__tenkz_kernel_basis_spacing_translation_count_int
      {
        ( 2 * \__tenkz_kernel_rows: + 1 )
        * ( 2 * \__tenkz_kernel_cols: + 1 )
      }
    \int_step_function:nnN {1}
      { \l__tenkz_kernel_basis_count_int - 1 }
      \__tenkz_kernel_basis_spacing_plan_a:n
  }

\cs_new_protected:Npn \__tenkz_kernel_basis_spacing_scan:n #1
  {
    \prop_get:NeN \l__tenkz_kernel_basis_live_record_prop
      { \int_use:N \l__tenkz_kernel_basis_spacing_scan_member_int / #1 }
      \l__tenkz_kernel_basis_live_record_tl
    \seq_set_split:NnV \l__tenkz_kernel_basis_spacing_address_seq {/}
      \l__tenkz_kernel_basis_live_record_tl
    \int_set:Nn \l__tenkz_kernel_basis_spacing_a_row_int
      { \seq_item:Nn \l__tenkz_kernel_basis_spacing_address_seq {1} }
    \int_set:Nn \l__tenkz_kernel_basis_spacing_a_col_int
      { \seq_item:Nn \l__tenkz_kernel_basis_spacing_address_seq {2} }
    \tl_set:Ne \l__tenkz_kernel_basis_spacing_a_id_tl
      { \seq_item:Nn \l__tenkz_kernel_basis_spacing_address_seq {3} }
    \prop_get:NeNT \l__tenkz_kernel_basis_live_prop
      {
        \int_use:N \l__tenkz_kernel_basis_spacing_target_member_int /
        \int_eval:n
          {
            \l__tenkz_kernel_basis_spacing_a_row_int
            + \l__tenkz_kernel_basis_spacing_scan_sign_int
              * \l__tenkz_kernel_basis_spacing_dr_int
          } /
        \int_eval:n
          {
            \l__tenkz_kernel_basis_spacing_a_col_int
            + \l__tenkz_kernel_basis_spacing_scan_sign_int
              * \l__tenkz_kernel_basis_spacing_dc_int
          }
      }
      \l__tenkz_kernel_basis_spacing_target_id_tl
      {
        \str_if_eq:eeF
          { \l__tenkz_kernel_basis_spacing_a_id_tl }
          { \l__tenkz_kernel_basis_spacing_target_id_tl }
          {
            \bool_set_true:N \l__tenkz_kernel_basis_spacing_realized_bool
            \prg_break:
          }
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_basis_spacing_dense_realized:nnnn
    #1#2#3#4
  {
    \int_set:Nn \l__tenkz_kernel_basis_spacing_a_int {#1}
    \int_set:Nn \l__tenkz_kernel_basis_spacing_b_int {#2}
    \int_set:Nn \l__tenkz_kernel_basis_spacing_dr_int {#3}
    \int_set:Nn \l__tenkz_kernel_basis_spacing_dc_int {#4}
    \bool_set_false:N \l__tenkz_kernel_basis_spacing_realized_bool
    \prop_get:NeNTF \l__tenkz_kernel_basis_live_in_window_count_prop {#1}
      \l_tmpa_tl
      { \int_set:Nn \l__tenkz_kernel_basis_spacing_a_count_int { \l_tmpa_tl } }
      { \int_zero:N \l__tenkz_kernel_basis_spacing_a_count_int }
    \prop_get:NeNTF \l__tenkz_kernel_basis_live_in_window_count_prop {#2}
      \l_tmpa_tl
      { \int_set:Nn \l__tenkz_kernel_basis_spacing_b_count_int { \l_tmpa_tl } }
      { \int_zero:N \l__tenkz_kernel_basis_spacing_b_count_int }
    \int_set:Nn \l__tenkz_kernel_basis_spacing_overlap_int
      {
        ( \__tenkz_kernel_rows: - \int_abs:n {#3} )
        * ( \__tenkz_kernel_cols: - \int_abs:n {#4} )
      }
    \int_set:Nn \l__tenkz_kernel_basis_spacing_holes_int
      {
        2 * \__tenkz_kernel_rows: * \__tenkz_kernel_cols:
        - \l__tenkz_kernel_basis_spacing_a_count_int
        - \l__tenkz_kernel_basis_spacing_b_count_int
      }
    \int_compare:nNnTF
      { \l__tenkz_kernel_basis_spacing_overlap_int }
      > { \l__tenkz_kernel_basis_spacing_holes_int }
      { \bool_set_true:N \l__tenkz_kernel_basis_spacing_realized_bool }
      {
        \prop_get:NeN \l__tenkz_kernel_basis_live_count_prop {#1} \l_tmpa_tl
        \int_set:Nn \l__tenkz_kernel_basis_spacing_a_count_int { \l_tmpa_tl }
        \prop_get:NeN \l__tenkz_kernel_basis_live_count_prop {#2} \l_tmpa_tl
        \int_set:Nn \l__tenkz_kernel_basis_spacing_b_count_int { \l_tmpa_tl }
        \int_compare:nNnTF
          { \l__tenkz_kernel_basis_spacing_a_count_int }
          < { \l__tenkz_kernel_basis_spacing_b_count_int + 1 }
          {
            \int_set:Nn \l__tenkz_kernel_basis_spacing_scan_member_int {#1}
            \int_set:Nn \l__tenkz_kernel_basis_spacing_target_member_int {#2}
            \int_set:Nn \l__tenkz_kernel_basis_spacing_scan_sign_int {1}
            \int_set_eq:NN \l_tmpa_int
              \l__tenkz_kernel_basis_spacing_a_count_int
          }
          {
            \int_set:Nn \l__tenkz_kernel_basis_spacing_scan_member_int {#2}
            \int_set:Nn \l__tenkz_kernel_basis_spacing_target_member_int {#1}
            \int_set:Nn \l__tenkz_kernel_basis_spacing_scan_sign_int {-1}
            \int_set_eq:NN \l_tmpa_int
              \l__tenkz_kernel_basis_spacing_b_count_int
          }
        \int_step_function:nnN {1} { \l_tmpa_int }
          \__tenkz_kernel_basis_spacing_scan:n
      }
  }

\prg_new_protected_conditional:Npnn
  \__tenkz_kernel_basis_spacing_query:nnnnn #1#2#3#4#5 { T, F, TF }
  {
    \str_if_eq:nnTF {#1} {candidate}
      {
        \bool_lazy_or:nnTF
          {
            \prop_if_in_p:Ne \l__tenkz_kernel_basis_spacing_plan_prop
              {dense/#2/#3}
          }
          {
            \prop_if_in_p:Ne \l__tenkz_kernel_basis_spacing_plan_prop
              {candidate/#2/#3/#4/#5}
          }
          { \prg_return_true: }
          { \prg_return_false: }
      }
      {
        \str_if_eq:nnTF {#1} {realized}
          {
            \prop_if_in:NeTF \l__tenkz_kernel_basis_spacing_plan_prop
              {dense/#2/#3}
              {
                \__tenkz_kernel_basis_spacing_dense_realized:nnnn
                  {#2}{#3}{#4}{#5}
                \bool_if:NTF \l__tenkz_kernel_basis_spacing_realized_bool
                  { \prg_return_true: }
                  { \prg_return_false: }
              }
              { \prg_return_true: }
          }
          { \prg_return_false: }
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_basis_spacing_report:
  {
    \tl_set:Ne \l__tenkz_kernel_basis_spacing_floor_tl
      {
        \__tenkz_geom_round:n
          {
            \prop_item:Nn \l__tenkz_kernel_basis_spacing_prop {floor}
          }
      }
    \fp_compare:nNnT
      { \prop_item:Nn \l__tenkz_kernel_basis_spacing_prop {clearance} }
      <
      {0}
      {
        \bool_set_true:N \l__tenkz_kernel_basis_spacing_bool
        \tl_set:Ne \l__tenkz_kernel_basis_spacing_dist_tl
          {
            \__tenkz_geom_round:n
              { \prop_item:Nn \l__tenkz_kernel_basis_spacing_prop {dist} }
          }
        \tl_set:Ne \l__tenkz_kernel_basis_spacing_margin_tl
          {
            \__tenkz_geom_round_margin:n
              {
                \prop_item:Nn
                  \l__tenkz_kernel_basis_spacing_prop {clearance}
              }
          }
        \exp_args:Nnneeee \msg_warning:nnnnnn
          {tenkz}{kernel-frame-basis-spacing}
          {
            pair~(
            \prop_item:Nn \l__tenkz_kernel_basis_spacing_prop {member-a}
            ,
            \prop_item:Nn \l__tenkz_kernel_basis_spacing_prop {member-b}
            )~at~B-minus-A~cell~translation~(
            \prop_item:Nn \l__tenkz_kernel_basis_spacing_prop {dr},
            \prop_item:Nn \l__tenkz_kernel_basis_spacing_prop {dc}
            )
          }
          { \l__tenkz_kernel_basis_spacing_dist_tl }
          { \l__tenkz_kernel_basis_spacing_floor_tl }
          { \l__tenkz_kernel_basis_spacing_margin_tl }
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_basis_spacing_prepare:
  {
    \bool_set_false:N \l__tenkz_kernel_basis_spacing_bool
    \prop_clear:N \l__tenkz_kernel_basis_spacing_prop
    \tl_clear:N \l__tenkz_kernel_basis_spacing_dist_tl
    \tl_clear:N \l__tenkz_kernel_basis_spacing_floor_tl
    \tl_clear:N \l__tenkz_kernel_basis_spacing_margin_tl
    \int_compare:nNnT { \l__tenkz_kernel_basis_count_int } > {1}
      {
        \__tenkz_geom_frame_if_affine:nT {kpic}
          {
            \__tenkz_kernel_basis_spacing_candidates:
            \__tenkz_geom_basis_spacing:nnnnnNNNN
              {kpic}
              { \l__tenkz_kernel_r_rows_int }
              { \l__tenkz_kernel_r_cols_int }
              { \__tenkz_metric_ratio:n {dotdia} / 2 }
              { \__tenkz_metric_default_bead_page_gap: }
              \l__tenkz_kernel_basis_spacing_east_prop
              \l__tenkz_kernel_basis_spacing_north_prop
              \__tenkz_kernel_basis_spacing_query:nnnnnTF
              \l__tenkz_kernel_basis_spacing_prop
            \prop_if_empty:NF \l__tenkz_kernel_basis_spacing_prop
              { \__tenkz_kernel_basis_spacing_report: }
          }
      }
  }

% Warning diagnostics are evaluated with the model, but their audit events
% must follow the picture header.  The parser is intentionally one-pass and
% rejects a warning that names a picture not yet declared.
\cs_new_protected:Npn \__tenkz_kernel_plane_events:
  {
    \str_if_eq:eeT { \__tenkz_kernel_r_frame_word: } {plane}
      {
        \fp_compare:nNnF
          { \__tenkz_metric_ratio:n {planerise} }
          > { \__tenkz_metric_ratio:n {physleg} }
          {
            \__tenkz_kernel_event:n
              {
                warning|picture=k\int_use:N \g__tenkz_kernel_picture_int
                |code=plane-rise-low
                |rise=\__tenkz_metric_ratio:n {planerise}
              }
          }
        \bool_lazy_and:nnF
          {
            \fp_compare_p:nNn
              { \__tenkz_metric_ratio:n {planeslant} }
              > { \__tenkz_metric_ratio:n {slantmin} }
          }
          {
            \fp_compare_p:nNn
              { \__tenkz_metric_ratio:n {planeslant} }
              < { \__tenkz_metric_ratio:n {slantmax} }
          }
          {
            \__tenkz_kernel_event:n
              {
                warning|picture=k\int_use:N \g__tenkz_kernel_picture_int
                |code=plane-slant-band
                |slant=\__tenkz_metric_ratio:n {planeslant}
              }
          }
        \fp_compare:nNnT
          {
            ( \__tenkz_kernel_rows: - 1 )
            * \__tenkz_metric_ratio:n {planeslant}
          }
          > { ( \__tenkz_kernel_cols: - 1 ) / 2 }
          {
            \__tenkz_kernel_event:n
              {
                warning|picture=k\int_use:N \g__tenkz_kernel_picture_int
                |code=plane-tall-window
                |rows=\__tenkz_kernel_rows:
                |cols=\__tenkz_kernel_cols:
              }
          }
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_basis_spacing_event:
  {
    \bool_if:NT \l__tenkz_kernel_basis_spacing_bool
      {
        \__tenkz_kernel_event:n
          {
            warning|picture=k\int_use:N \g__tenkz_kernel_picture_int
            |code=basis-spacing
            |member-a=\prop_item:Nn
              \l__tenkz_kernel_basis_spacing_prop {member-a}
            |member-b=\prop_item:Nn
              \l__tenkz_kernel_basis_spacing_prop {member-b}
            |dr=\prop_item:Nn \l__tenkz_kernel_basis_spacing_prop {dr}
            |dc=\prop_item:Nn \l__tenkz_kernel_basis_spacing_prop {dc}
            |dist=\l__tenkz_kernel_basis_spacing_dist_tl
            |floor=\l__tenkz_kernel_basis_spacing_floor_tl
            |margin=\l__tenkz_kernel_basis_spacing_margin_tl
          }
      }
  }

% The turn a record's carrier gives it: the angle its own axes stand at on
% the page.  Ports read their faces in it, skins are inked in it, and the
% hull measures silhouettes in it.
\cs_new_protected:Npn \__tenkz_kernel_r_turn:nN #1#2
  {
    \prop_get:NnN \l__tenkz_kernel_r_turn_prop {#1} #2
    \quark_if_no_value:NT #2 { \tl_set:Nn #2 {0} }
  }
\prop_new:N \l__tenkz_kernel_r_turn_prop

% The complete basis carried by one rendered atom.  Cell atoms and cluster
% children resolve through the same carrier lookup used by projected labels;
% page-space atoms retain their scalar-turn basis.  The dual is cached beside
% the basis for inverse consumers such as local anchors and upright labels.
% The plane result is a declared frame fact, not a floating-point guess about
% whether the returned vectors happen to be orthogonal.
\tl_new:N \l__tenkz_kernel_r_basis_ex_tl
\tl_new:N \l__tenkz_kernel_r_basis_ey_tl
\tl_new:N \l__tenkz_kernel_r_basis_nx_tl
\tl_new:N \l__tenkz_kernel_r_basis_ny_tl
\tl_new:N \l__tenkz_kernel_r_basis_ux_tl
\tl_new:N \l__tenkz_kernel_r_basis_uy_tl
\tl_new:N \l__tenkz_kernel_r_basis_vx_tl
\tl_new:N \l__tenkz_kernel_r_basis_vy_tl
\tl_new:N \l__tenkz_kernel_r_basis_turn_tl
\bool_new:N \l__tenkz_kernel_r_basis_plane_bool
\cs_new_protected:Npn \__tenkz_kernel_r_carrier_basis:n #1
  {
    \bool_set_false:N \l__tenkz_kernel_r_basis_plane_bool
    \__tenkz_kernel_r_record_carrier:nNNN {#1}
      \l__tenkz_kernel_turn_a_tl \l__tenkz_kernel_turn_b_tl
      \l__tenkz_kernel_turn_bool
    \bool_if:NTF \l__tenkz_kernel_turn_bool
      {
        \__tenkz_geom_local_basis:nnnNNNN {kpic}
          { \l__tenkz_kernel_turn_a_tl }
          { \l__tenkz_kernel_turn_b_tl }
          \l__tenkz_kernel_r_basis_ex_tl
          \l__tenkz_kernel_r_basis_ey_tl
          \l__tenkz_kernel_r_basis_nx_tl
          \l__tenkz_kernel_r_basis_ny_tl
        \str_if_eq:eeT { \__tenkz_kernel_r_frame_word: } {plane}
          { \bool_set_true:N \l__tenkz_kernel_r_basis_plane_bool }
      }
      {
        \__tenkz_kernel_r_turn:nN {#1}
          \l__tenkz_kernel_r_basis_turn_tl
        \tl_set:Ne \l__tenkz_kernel_r_basis_ex_tl
          { \fp_eval:n { cosd(\l__tenkz_kernel_r_basis_turn_tl) } }
        \tl_set:Ne \l__tenkz_kernel_r_basis_ey_tl
          { \fp_eval:n { sind(\l__tenkz_kernel_r_basis_turn_tl) } }
        \tl_set:Ne \l__tenkz_kernel_r_basis_nx_tl
          { \fp_eval:n { -sind(\l__tenkz_kernel_r_basis_turn_tl) } }
        \tl_set:Ne \l__tenkz_kernel_r_basis_ny_tl
          { \fp_eval:n { cosd(\l__tenkz_kernel_r_basis_turn_tl) } }
      }
    \__tenkz_geom_basis_dual:nnnnNNNN
      { \l__tenkz_kernel_r_basis_ex_tl }
      { \l__tenkz_kernel_r_basis_ey_tl }
      { \l__tenkz_kernel_r_basis_nx_tl }
      { \l__tenkz_kernel_r_basis_ny_tl }
      \l__tenkz_kernel_r_basis_ux_tl
      \l__tenkz_kernel_r_basis_uy_tl
      \l__tenkz_kernel_r_basis_vx_tl
      \l__tenkz_kernel_r_basis_vy_tl
  }

% Append the one sanctioned glyph transform for a record's carrier.  Plane
% contours consume their declared complete basis; flat and circular carriers
% retain the established scalar-turn path.
\cs_new_protected:Npn \__tenkz_kernel_r_glyph_frame:nN #1#2
  {
    \__tenkz_kernel_r_carrier_basis:n {#1}
    \bool_if:NTF \l__tenkz_kernel_r_basis_plane_bool
      {
        \tl_put_right:Ne #2
          {
            , tenkz~glyph~basis = {
                \l__tenkz_kernel_r_basis_ex_tl ,
                \l__tenkz_kernel_r_basis_ey_tl ,
                \l__tenkz_kernel_r_basis_nx_tl ,
                \l__tenkz_kernel_r_basis_ny_tl
              }
          }
      }
      {
        \__tenkz_kernel_r_turn:nN {#1} \l__tenkz_kernel_r_ink_turn_tl
        \fp_compare:nNnF { \l__tenkz_kernel_r_ink_turn_tl } = {0}
          {
            \tl_put_right:Ne #2
              {
                , tenkz~glyph~frame =
                    \tl_use:N \l__tenkz_kernel_r_ink_turn_tl
              }
          }
      }
  }

% Carry one of an atom's logical face directions to the page.  Kernel cell
% coordinates are (row, column), whereas the face is measured from local east.
% Geometry owns the complete east/north basis at that address and carries the
% direction through it.  A non-cell atom retains its carrier-local turn model.
\cs_new_protected:Npn \__tenkz_kernel_r_page_face:nnN #1#2#3
  {
    \__tenkz_kernel_atom_rc:nNNN {#1}
      \l__tenkz_kernel_turn_a_tl \l__tenkz_kernel_turn_b_tl
      \l__tenkz_kernel_turn_bool
    \bool_if:NTF \l__tenkz_kernel_turn_bool
      {
        \__tenkz_geom_local_bearing:nnnnN {kpic}
          { \l__tenkz_kernel_turn_a_tl }
          { \l__tenkz_kernel_turn_b_tl }
          {#2} #3
      }
      {
        \__tenkz_kernel_r_turn:nN {#1} \l__tenkz_kernel_turn_tl
        \tl_set:Ne #3
          { \fp_eval:n { (#2) + \l__tenkz_kernel_turn_tl } }
      }
  }

% Read a page-space vector in atom #1's own carrier coordinates.  Consumers
% use this inverse alongside `r_page_face`, rather than reaching into the
% carrier-basis scratch state or reproducing affine/circle cases themselves.
\cs_new_protected:Npn \__tenkz_kernel_r_local_vector:nnnNN #1#2#3#4#5
  {
    \__tenkz_kernel_r_carrier_basis:n {#1}
    \__tenkz_geom_basis_unapply:nnnnnnNN
      { \l__tenkz_kernel_r_basis_ex_tl }
      { \l__tenkz_kernel_r_basis_ey_tl }
      { \l__tenkz_kernel_r_basis_nx_tl }
      { \l__tenkz_kernel_r_basis_ny_tl }
      {#2} {#3} #4 #5
  }

% The physical policy follows the carrier's physical axis, not an authored
% in-plane face.  A projected plane owns a third, page-space transverse
% vector.  Its glyph exit is the ray intersection obtained by pulling that
% vector back through the atom's complete carrier basis.  Flat and circular
% carriers retain their ordinary local north/south faces.
\tl_new:N \l__tenkz_kernel_r_physical_x_tl
\tl_new:N \l__tenkz_kernel_r_physical_y_tl
\tl_new:N \l__tenkz_kernel_r_physical_u_tl
\tl_new:N \l__tenkz_kernel_r_physical_v_tl
\tl_new:N \l__tenkz_kernel_r_physical_anchor_tl
\cs_new_protected:Npn \__tenkz_kernel_r_physical_axis:nnNN #1#2#3#4
  {
    \__tenkz_kernel_r_carrier_basis:n {#1}
    \bool_if:NTF \l__tenkz_kernel_r_basis_plane_bool
      {
        \__tenkz_geom_frame_transverse:nnNN {kpic} {#2}
          \l__tenkz_kernel_r_physical_x_tl
          \l__tenkz_kernel_r_physical_y_tl
        \tl_set:Ne #3
          {
            \fp_eval:n
              {
                atand(
                  \l__tenkz_kernel_r_physical_y_tl ,
                  \l__tenkz_kernel_r_physical_x_tl )
              }
          }
        \__tenkz_geom_basis_unapply:nnnnnnNN
          { \l__tenkz_kernel_r_basis_ex_tl }
          { \l__tenkz_kernel_r_basis_ey_tl }
          { \l__tenkz_kernel_r_basis_nx_tl }
          { \l__tenkz_kernel_r_basis_ny_tl }
          { \l__tenkz_kernel_r_physical_x_tl }
          { \l__tenkz_kernel_r_physical_y_tl }
          \l__tenkz_kernel_r_physical_u_tl
          \l__tenkz_kernel_r_physical_v_tl
        \tl_set:Ne #4
          {
            \fp_eval:n
              {
                atand(
                  \l__tenkz_kernel_r_physical_v_tl ,
                  \l__tenkz_kernel_r_physical_u_tl )
              }
          }
      }
      {
        \__tenkz_kernel_r_page_face:nnN {#1}
          { \__tenkz_kernel_policy_face_bearing:n {#2} } #3
        \tl_set:Ne #4 { \__tenkz_kernel_policy_face_bearing:n {#2} }
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_r_frame_xy:nnNN #1#2#3#4
  {
    \__tenkz_geom_apply:nnnNN {kpic} {#1} {#2}
      \l__tenkz_kernel_r_x_tl \l__tenkz_kernel_r_y_tl
    \fp_set:Nn #3 { \l__tenkz_kernel_r_x_tl + 0 }
    \fp_set:Nn #4 { \l__tenkz_kernel_r_y_tl + 0 }
  }

% The turn of one atom, read after it is placed: the frame's own turn at the
% cell it occupies, or -- for a spin that stands on a cluster's sub-frame --
% the turn of the carrier it stands on.  Only a turned atom is recorded, so
% a flat picture stores nothing and asks nothing.  These registers are this
% walk's alone: it runs over every atom and must not touch the shared
% scratch the placement below is mid-computation on.
\tl_new:N \l__tenkz_kernel_turn_a_tl
\tl_new:N \l__tenkz_kernel_turn_b_tl
\tl_new:N \l__tenkz_kernel_turn_tl
\bool_new:N \l__tenkz_kernel_turn_bool
\cs_new_protected:Npn \__tenkz_kernel_r_atom_turn:n #1
  {
    \__tenkz_kernel_atom_rc:nNNN {#1}
      \l__tenkz_kernel_turn_a_tl \l__tenkz_kernel_turn_b_tl
      \l__tenkz_kernel_turn_bool
    \bool_if:NTF \l__tenkz_kernel_turn_bool
      {
        \__tenkz_geom_turn:nnnN {kpic}
          { \l__tenkz_kernel_turn_a_tl } { \l__tenkz_kernel_turn_b_tl }
          \l__tenkz_kernel_turn_tl
      }
      {
        \tl_set:Nn \l__tenkz_kernel_turn_tl {0}
        \__tenkz_model_get:nnN {#1} {cluster-of} \l__tenkz_kernel_turn_a_tl
        \quark_if_no_value:NF \l__tenkz_kernel_turn_a_tl
          {
            \prop_get:NVN \l__tenkz_kernel_named_prop
              \l__tenkz_kernel_turn_a_tl \l__tenkz_kernel_turn_b_tl
            \quark_if_no_value:NF \l__tenkz_kernel_turn_b_tl
              {
                \exp_args:NV \__tenkz_kernel_r_turn:nN
                  \l__tenkz_kernel_turn_b_tl \l__tenkz_kernel_turn_tl
              }
          }
      }
    \fp_compare:nNnF { \l__tenkz_kernel_turn_tl } = {0}
      {
        \prop_put:NnV \l__tenkz_kernel_r_turn_prop
          {#1} \l__tenkz_kernel_turn_tl
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_r_atom_place_span:n #1
  {
    \__tenkz_kernel_r_span:nNN {#1}
      \l__tenkz_kernel_r_b_tl \l__tenkz_kernel_r_c_tl
    \__tenkz_geom_place_cell:nnee {#1} {kpic}
      {
        \fp_eval:n
          {
            \l__tenkz_kernel_r_row_tl
            + ( \l__tenkz_kernel_r_c_tl - 1 ) / 2
          }
      }
      {
        \fp_eval:n
          {
            \l__tenkz_kernel_r_col_tl
            + ( \l__tenkz_kernel_r_b_tl - 1 ) / 2
          }
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_r_atom_place_member_span:n #1
  {
    \__tenkz_model_get:nnN
      {#1} {basis-east} \l__tenkz_kernel_r_x_tl
    \__tenkz_model_get:nnN
      {#1} {basis-north} \l__tenkz_kernel_r_y_tl
    \__tenkz_kernel_r_span:nNN {#1}
      \l__tenkz_kernel_r_b_tl \l__tenkz_kernel_r_c_tl
    \__tenkz_geom_place_cell:nnee {#1} {kpic}
      {
        \fp_eval:n
          {
            \l__tenkz_kernel_r_row_tl
            + ( \l__tenkz_kernel_r_c_tl - 1 ) / 2
            - \l__tenkz_kernel_r_y_tl
              * \c__tenkz_kernel_basis_step_fp
          }
      }
      {
        \fp_eval:n
          {
            \l__tenkz_kernel_r_col_tl
            + ( \l__tenkz_kernel_r_b_tl - 1 ) / 2
            + \l__tenkz_kernel_r_x_tl
              * \c__tenkz_kernel_basis_step_fp
          }
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_r_atom_place:n #1
  {
    \__tenkz_model_get:nnN {#1} {addr} \l__tenkz_kernel_r_tl
    \quark_if_no_value:NTF \l__tenkz_kernel_r_tl
      {
        \__tenkz_model_get:nnN {#1} {node} \l__tenkz_kernel_r_tl
        \quark_if_no_value:NTF \l__tenkz_kernel_r_tl
          {
            \__tenkz_model_get:nnN {#1} {cluster-of} \l__tenkz_kernel_r_tl
            \quark_if_no_value:NTF \l__tenkz_kernel_r_tl
              { \msg_error:nnn {tenkz}{kernel-render-unplaced} {#1} }
              { \__tenkz_kernel_r_cluster_place:n {#1} }
          }
          {
            \tl_set:NV \l__tenkz_kernel_r_node_tl \l__tenkz_kernel_r_tl
            \__tenkz_kernel_atom_rc:nNNN {#1}
              \l__tenkz_kernel_r_row_tl \l__tenkz_kernel_r_col_tl
              \l_tmpa_bool
            \bool_if:NTF \l_tmpa_bool
              { \__tenkz_kernel_r_atom_place_span:n {#1} }
              {
                \__tenkz_kernel_r_node:V \l__tenkz_kernel_r_node_tl
                \__tenkz_geom_place_rel:nVee {#1}
                  \l__tenkz_kernel_r_node_tl {0} {0}
              }
          }
      }
      {
        \exp_args:NV \__tenkz_kernel_r_rc:nNN \l__tenkz_kernel_r_tl
          \l__tenkz_kernel_r_row_tl \l__tenkz_kernel_r_col_tl
        \__tenkz_model_get:nnN {#1} {member} \l__tenkz_kernel_r_b_tl
        \quark_if_no_value:NTF \l__tenkz_kernel_r_b_tl
          { \__tenkz_kernel_r_atom_place_span:n {#1} }
          {
            \__tenkz_kernel_basis_cell_aligned:TF
              { \__tenkz_kernel_r_atom_place_span:n {#1} }
              { \__tenkz_kernel_r_atom_place_member_span:n {#1} }
          }
      }
  }

% A cluster sub-atom sits on the quarter-pitch sub-frame of its parent; its
% own name carries its (r, c) inside the cluster by construction.  The
% sub-frame is the parent's, so the step from the parent to the member is
% taken in the parent's axes: four spins on a turned carrier turn with it
% rather than staying square to the page.  The complete basis carries that
% step: a scalar turn would discard shear and unequal axis scale.
\tl_new:N \l__tenkz_kernel_cluster_ex_tl
\tl_new:N \l__tenkz_kernel_cluster_ey_tl
\tl_new:N \l__tenkz_kernel_cluster_nx_tl
\tl_new:N \l__tenkz_kernel_cluster_ny_tl
\tl_new:N \l__tenkz_kernel_cluster_dx_tl
\tl_new:N \l__tenkz_kernel_cluster_dy_tl
\cs_new_protected:Npn \__tenkz_kernel_r_cluster_place:n #1
  {
    % parent record id
    \prop_get:NVN \l__tenkz_kernel_named_prop \l__tenkz_kernel_r_tl
      \l__tenkz_kernel_r_b_tl
    \exp_args:NV \__tenkz_kernel_r_atom_turn:n \l__tenkz_kernel_r_b_tl
    % cluster extent RxC from the parent record
    \__tenkz_model_get:nnN
      { \tl_use:N \l__tenkz_kernel_r_b_tl } {cluster} \l__tenkz_kernel_r_c_tl
    \exp_args:NV \__tenkz_kernel_r_rc:nNN \l__tenkz_kernel_r_c_tl
      \l__tenkz_kernel_r_row_tl \l__tenkz_kernel_r_col_tl
    % own (r, c): the last two dash-separated fields of the name
    \__tenkz_model_get:nnN {#1} {name} \l__tenkz_kernel_r_c_tl
    \exp_args:NnV \regex_extract_once:nnN { (\d+) - (\d+) \Z }
      \l__tenkz_kernel_r_c_tl \l__tenkz_kernel_match_seq
    \fp_set:Nn \l__tenkz_kernel_r_x_fp
      {
        ( \seq_item:Nn \l__tenkz_kernel_match_seq {3}
          - ( \l__tenkz_kernel_r_col_tl + 1 ) / 2 )
        * \__tenkz_metric_ratio:n {clusterstep}
      }
    \fp_set:Nn \l__tenkz_kernel_r_y_fp
      {
        - ( \seq_item:Nn \l__tenkz_kernel_match_seq {2}
            - ( \l__tenkz_kernel_r_row_tl + 1 ) / 2 )
        * \__tenkz_metric_ratio:n {clusterstep}
      }
    \exp_args:NV \__tenkz_kernel_atom_rc:nNNN
      \l__tenkz_kernel_r_b_tl
      \l__tenkz_kernel_r_row_tl \l__tenkz_kernel_r_col_tl
      \l__tenkz_kernel_turn_bool
    \bool_if:NTF \l__tenkz_kernel_turn_bool
      {
        \__tenkz_geom_local_basis:nnnNNNN {kpic}
          { \l__tenkz_kernel_r_row_tl } { \l__tenkz_kernel_r_col_tl }
          \l__tenkz_kernel_cluster_ex_tl
          \l__tenkz_kernel_cluster_ey_tl
          \l__tenkz_kernel_cluster_nx_tl
          \l__tenkz_kernel_cluster_ny_tl
      }
      {
        \tl_set:Ne \l__tenkz_kernel_cluster_ex_tl
          { \fp_eval:n { cosd(\l__tenkz_kernel_turn_tl) } }
        \tl_set:Ne \l__tenkz_kernel_cluster_ey_tl
          { \fp_eval:n { sind(\l__tenkz_kernel_turn_tl) } }
        \tl_set:Ne \l__tenkz_kernel_cluster_nx_tl
          { \fp_eval:n { -sind(\l__tenkz_kernel_turn_tl) } }
        \tl_set:Ne \l__tenkz_kernel_cluster_ny_tl
          { \fp_eval:n { cosd(\l__tenkz_kernel_turn_tl) } }
      }
    \__tenkz_geom_basis_apply:nnnnnnNN
      { \l__tenkz_kernel_cluster_ex_tl }
      { \l__tenkz_kernel_cluster_ey_tl }
      { \l__tenkz_kernel_cluster_nx_tl }
      { \l__tenkz_kernel_cluster_ny_tl }
      { \l__tenkz_kernel_r_x_fp } { \l__tenkz_kernel_r_y_fp }
      \l__tenkz_kernel_cluster_dx_tl \l__tenkz_kernel_cluster_dy_tl
    \__tenkz_geom_place_offset:nVee {#1} \l__tenkz_kernel_r_b_tl
      { \l__tenkz_kernel_cluster_dx_tl }
      { \l__tenkz_kernel_cluster_dy_tl }
  }

% ---------- the address dependency graph --------------------------------------
% "Address resolution is a dependency graph evaluated in one pass; a cycle is
% the coded error [TKZ-ADDR-CYCLE]" -- and the graph is over records, not over
% the passes that happen to draw them.  A record's place may name other
% records: a tensor standing at a point of a curve depends on that curve, and
% a curve whose end is a tensor depends on that tensor.  Settling a record
% settles everything its place names first, so the order is read off the
% picture rather than fixed here.  A record met a second time on one descent
% is a circular place, printed with the trail that closed on it.
%
% A curve exists only once the wire stage has drawn it, and a record whose
% place names a curve therefore cannot settle before that stage runs.  Such a
% record is left unsettled and the sweep after the wire stage takes it.  This
% is a property of curves, not of tensors: nothing below asks what class a
% record belongs to in order to decide whether it may settle.
%
% These registers belong to the walk alone.  Settling calls placement, which
% is mid-computation on the shared r_ scratch, so the walk touches none of it.
\prop_new:N \l__tenkz_kernel_dep_state_prop  % record id -> visiting | settled
\seq_new:N  \l__tenkz_kernel_dep_trail_seq   % the descent, in author's names
\tl_new:N   \l__tenkz_kernel_dep_acc_tl      % the list being collected
\tl_new:N   \l__tenkz_kernel_dep_tl          % one collected list, by value
\tl_new:N   \l__tenkz_kernel_dep_look_tl     % one field read
\bool_new:N \l__tenkz_kernel_dep_ready_bool
\int_new:N  \l__tenkz_kernel_dep_stage_int   % 0 before the wire stage, 1 in
                                             % it, 2 after it

% The wire stage itself is a node of the graph: a policy leg is a wire that
% owns no record, so an address naming one depends on this instead.
\tl_const:Nn \c__tenkz_kernel_dep_wires_tl { tenkz-wire-stage }

\msg_new:nnn {tenkz}{kernel-addr-cycle}
  { [TKZ-ADDR-CYCLE]~these~places~depend~on~themselves:~#1 }

% A record answers to its author's name where it has one, and to its own id
% where it does not; a diagnostic quotes the spelling the author can find.
\cs_new_protected:Npn \__tenkz_kernel_dep_label:nN #1#2
  {
    \__tenkz_model_get:nnN {#1} {name} #2
    \quark_if_no_value:NT #2 { \tl_set:Nn #2 {#1} }
  }

\cs_new_protected:Npn \__tenkz_kernel_dep_add:n #1
  {
    \prop_get:NnN \l__tenkz_kernel_named_prop {#1} \l__tenkz_kernel_dep_look_tl
    \quark_if_no_value:NF \l__tenkz_kernel_dep_look_tl
      {
        \tl_put_right:Ne \l__tenkz_kernel_dep_acc_tl
          { { \tl_use:N \l__tenkz_kernel_dep_look_tl } }
      }
  }
\cs_generate_variant:Nn \__tenkz_kernel_dep_add:n { e }

% the record occupying the cell an address names, where one does
\cs_new_protected:Npn \__tenkz_kernel_dep_cell:n #1
  {
    \prop_get:NeN \l__tenkz_kernel_cell_prop
      {
        \__tenkz_kernel_r_item:nn {#1} {row} -
        \__tenkz_kernel_r_item:nn {#1} {col}
      }
      \l__tenkz_kernel_dep_look_tl
    \quark_if_no_value:NF \l__tenkz_kernel_dep_look_tl
      {
        \tl_put_right:Ne \l__tenkz_kernel_dep_acc_tl
          { { \tl_use:N \l__tenkz_kernel_dep_look_tl } }
      }
  }
\cs_generate_variant:Nn \__tenkz_kernel_dep_cell:n { e }

% Every production is asked the same question -- which records does this
% place name? -- and each answers from its own operands.
\cs_new_protected:Npn \__tenkz_kernel_dep_node:n #1
  {
    \str_case:enF { \__tenkz_kernel_r_item:nn {#1} {kind} }
      {
        {rel}
          {
            \__tenkz_kernel_dep_node:e
              { \__tenkz_kernel_r_item:nn {#1} {base} }
          }
        {mid}
          {
            \__tenkz_kernel_dep_node:e
              { \__tenkz_kernel_r_item:nn {#1} {first} }
            \__tenkz_kernel_dep_node:e
              { \__tenkz_kernel_r_item:nn {#1} {second} }
          }
        {crossing}
          {
            \__tenkz_kernel_dep_node:e
              { \__tenkz_kernel_r_item:nn {#1} {first} }
            \__tenkz_kernel_dep_node:e
              { \__tenkz_kernel_r_item:nn {#1} {second} }
          }
        {record}
          { \__tenkz_kernel_dep_add:e { \__tenkz_kernel_r_item:nn {#1} {name} } }
        {member}
          {
            \__tenkz_kernel_basis_record:eeeN
              { \__tenkz_kernel_r_item:nn {#1} {row} }
              { \__tenkz_kernel_r_item:nn {#1} {col} }
              { \__tenkz_kernel_r_item:nn {#1} {member} }
              \l__tenkz_kernel_dep_look_tl
            \quark_if_no_value:NF \l__tenkz_kernel_dep_look_tl
              {
                \tl_put_right:Ne \l__tenkz_kernel_dep_acc_tl
                  { { \tl_use:N \l__tenkz_kernel_dep_look_tl } }
              }
          }
        {port}
          {
            \prop_get:NeN \l__tenkz_kernel_node_prop {#1/record-id}
              \l__tenkz_kernel_dep_look_tl
            \quark_if_no_value:NTF \l__tenkz_kernel_dep_look_tl
              {
                \__tenkz_kernel_dep_add:e
                  { \__tenkz_kernel_r_item:nn {#1} {record} }
              }
              {
                \tl_put_right:Ne \l__tenkz_kernel_dep_acc_tl
                  { { \tl_use:N \l__tenkz_kernel_dep_look_tl } }
              }
          }
        {onwire}
          { \__tenkz_kernel_dep_add:e { \__tenkz_kernel_r_item:nn {#1} {wire} } }
        {leg}
          {
            \__tenkz_kernel_dep_cell:e { \__tenkz_kernel_r_item:nn {#1} {cell} }
            \tl_put_right:Ne \l__tenkz_kernel_dep_acc_tl
              { { \c__tenkz_kernel_dep_wires_tl } }
          }
      }
      { }
  }
\cs_generate_variant:Nn \__tenkz_kernel_dep_node:n { e, V }

% The records one record's place names: the carrier it stands on, the
% addresses of its own place and of its ends, and the waypoints it travels.
\cs_new_protected:Npn \__tenkz_kernel_dep_list:nN #1#2
  {
    \tl_clear:N \l__tenkz_kernel_dep_acc_tl
    \__tenkz_model_get:nnN {#1} {cluster-of} \l__tenkz_kernel_dep_look_tl
    \quark_if_no_value:NF \l__tenkz_kernel_dep_look_tl
      { \exp_args:NV \__tenkz_kernel_dep_add:n \l__tenkz_kernel_dep_look_tl }
    \clist_map_inline:nn {node, from, to}
      {
        \__tenkz_model_get:nnN {#1} {##1} \l__tenkz_kernel_dep_look_tl
        \quark_if_no_value:NF \l__tenkz_kernel_dep_look_tl
          { \__tenkz_kernel_dep_node:V \l__tenkz_kernel_dep_look_tl }
      }
    \__tenkz_model_get:nnN {#1} {via} \l__tenkz_kernel_dep_look_tl
    \quark_if_no_value:NF \l__tenkz_kernel_dep_look_tl
      {
        \exp_args:NV \clist_map_inline:nn \l__tenkz_kernel_dep_look_tl
          { \__tenkz_kernel_dep_waypoint:n {##1} }
      }
    \tl_set_eq:NN #2 \l__tenkz_kernel_dep_acc_tl
  }
% A waypoint keeps the author's spelling in its record, so it is read through
% the one address parser rather than a second reading of the grammar.
\cs_new_protected:Npn \__tenkz_kernel_dep_waypoint:n #1
  {
    \__tenkz_kernel_addr_operand:nN {#1} \l__tenkz_kernel_dep_look_tl
    \__tenkz_kernel_dep_node:V \l__tenkz_kernel_dep_look_tl
  }

\cs_new_protected:Npn \__tenkz_kernel_dep_reset:
  {
    \prop_clear:N \l__tenkz_kernel_dep_state_prop
    \seq_clear:N \l__tenkz_kernel_dep_trail_seq
    \int_zero:N \l__tenkz_kernel_dep_stage_int
  }

\cs_new_protected:Npn \__tenkz_kernel_settle:n #1
  {
    \prop_get:NnN \l__tenkz_kernel_dep_state_prop {#1}
      \l__tenkz_kernel_dep_look_tl
    \quark_if_no_value:NTF \l__tenkz_kernel_dep_look_tl
      { \__tenkz_kernel_settle_visit:n {#1} }
      {
        \str_if_eq:VnF \l__tenkz_kernel_dep_look_tl {settled}
          {
            % the record the descent returned to closes the trail, so the
            % diagnostic reads as the circle the author wrote
            \__tenkz_kernel_dep_label:nN {#1} \l__tenkz_kernel_dep_look_tl
            \seq_put_right:NV \l__tenkz_kernel_dep_trail_seq
              \l__tenkz_kernel_dep_look_tl
            \msg_error:nne {tenkz}{kernel-addr-cycle}
              { \seq_use:Nn \l__tenkz_kernel_dep_trail_seq { ~->~ } }
            \seq_pop_right:NN \l__tenkz_kernel_dep_trail_seq
              \l__tenkz_kernel_dep_look_tl
            % the trail is reported once; the descent unwinds rather than
            % turning a circular place into an unbounded one
            \prop_put:Nnn \l__tenkz_kernel_dep_state_prop {#1} {settled}
          }
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_settle_visit:n #1
  {
    \prop_put:Nnn \l__tenkz_kernel_dep_state_prop {#1} {visiting}
    \__tenkz_kernel_dep_label:nN {#1} \l__tenkz_kernel_dep_look_tl
    \seq_put_right:NV \l__tenkz_kernel_dep_trail_seq
      \l__tenkz_kernel_dep_look_tl
    \__tenkz_kernel_dep_list:nN {#1} \l__tenkz_kernel_dep_tl
    % the list travels by value: the descent below reuses the register
    \exp_args:NV \__tenkz_kernel_settle_after:nn
      \l__tenkz_kernel_dep_tl {#1}
  }
% #1 the records #2 depends on, #2 the record.  A record settles when every
% record it names has settled, and stays unsettled otherwise.
\cs_new_protected:Npn \__tenkz_kernel_settle_after:nn #1#2
  {
    \tl_map_inline:nn {#1} { \__tenkz_kernel_settle:n {##1} }
    \prop_remove:Nn \l__tenkz_kernel_dep_state_prop {#2}
    \seq_pop_right:NN \l__tenkz_kernel_dep_trail_seq
      \l__tenkz_kernel_dep_look_tl
    \bool_set_true:N \l__tenkz_kernel_dep_ready_bool
    \tl_map_inline:nn {#1}
      {
        \prop_get:NnN \l__tenkz_kernel_dep_state_prop {##1}
          \l__tenkz_kernel_dep_look_tl
        \bool_lazy_or:nnT
          { \quark_if_no_value_p:N \l__tenkz_kernel_dep_look_tl }
          { ! \str_if_eq_p:Vn \l__tenkz_kernel_dep_look_tl {settled} }
          { \bool_set_false:N \l__tenkz_kernel_dep_ready_bool }
      }
    \bool_if:NT \l__tenkz_kernel_dep_ready_bool
      { \__tenkz_kernel_settle_realize:n {#2} }
  }
% Realizing a record is giving it the geometry its class carries: an atom
% takes its place, a curve is routed.  A record whose geometry the wire stage
% owns realizes only once that stage has begun; the wire stage itself is such
% a record, and the render pass settles it when it reaches that point.
\cs_new_protected:Npn \__tenkz_kernel_settle_realize:n #1
  {
    \prop_get:NnN \l__tenkz_model_class_prop {#1} \l__tenkz_kernel_dep_look_tl
    \str_case:VnF \l__tenkz_kernel_dep_look_tl
      {
        {atom}
          {
            \__tenkz_kernel_r_atom_place:n {#1}
            \prop_put:Nnn \l__tenkz_kernel_dep_state_prop {#1} {settled}
          }
        {wire}
          {
            \__tenkz_model_get:nnN {#1} {kind}
              \l__tenkz_kernel_dep_look_tl
            \bool_lazy_or:nnTF
              { \str_if_eq_p:Vn \l__tenkz_kernel_dep_look_tl {string} }
              { \str_if_eq_p:Vn \l__tenkz_kernel_dep_look_tl {pairing} }
              {
                \int_compare:nNnT { \l__tenkz_kernel_dep_stage_int } > {0}
                  {
                    \__tenkz_kernel_r_wire_route:n {#1}
                    \prop_put:Nnn
                      \l__tenkz_kernel_dep_state_prop {#1} {settled}
                  }
              }
              {
                \__tenkz_model_get:nnN {#1} {from}
                  \l__tenkz_kernel_ow_b_tl
                \__tenkz_model_get:nnN {#1} {to}
                  \l__tenkz_kernel_ow_c_tl
                \__tenkz_model_get:nnN {#1} {origin}
                  \l__tenkz_kernel_dep_look_tl
                \bool_lazy_all:nTF
                  {
                    { ! \quark_if_no_value_p:N \l__tenkz_kernel_ow_b_tl }
                    { ! \quark_if_no_value_p:N \l__tenkz_kernel_ow_c_tl }
                    % A closure owns its route: the renderer chooses the shape
                    % that leaves the frame, so two ends do not describe it.
                    { ! \str_if_eq_p:Vn \l__tenkz_kernel_dep_look_tl {cup} }
                    { ! \str_if_eq_p:Vn \l__tenkz_kernel_dep_look_tl {trace} }
                  }
                  {
                    % A closed straight index wire has no staged curve; its
                    % endpoints already determine every `on wire t` point.
                    \prop_put:Nnn
                      \l__tenkz_kernel_dep_state_prop {#1} {settled}
                  }
                  {
                    % Open and generated index paths may depend on measured
                    % carrier hulls, so they retain the ordinary wire-stage
                    % boundary.
                    \int_compare:nNnT
                      { \l__tenkz_kernel_dep_stage_int } > {0}
                      {
                        \prop_put:Nnn
                          \l__tenkz_kernel_dep_state_prop {#1} {settled}
                      }
                  }
              }
          }
      }
      { }
  }
% The route a wire declares before anything is drawn.  Only a curve declares
% one here; an index wire's route is the path its own ink saves, which is why
% a closure waits for the wire stage instead of settling on its two ends.
\cs_new_protected:Npn \__tenkz_kernel_r_wire_route:n #1
  {
    \__tenkz_model_get:nnN {#1} {kind} \l__tenkz_kernel_dep_look_tl
    \str_case:Vn \l__tenkz_kernel_dep_look_tl
      {
        {string}  { \__tenkz_kernel_r_string_declare:n {#1} }
        {pairing} { \__tenkz_kernel_r_skin_wire_prepare:n {#1} }
      }
  }

% Resolve one address node into the placement graph.  Field reads happen
% AFTER any recursion, so no scratch register must survive a recursive call.
\cs_new_protected:Npn \__tenkz_kernel_r_node:n #1
  {
    \prop_if_in:NnF \l__tenkz_kernel_r_placed_prop {#1}
      {
        \prop_put:Nnn \l__tenkz_kernel_r_placed_prop {#1} { }
        \exp_args:Ne \__tenkz_kernel_r_node_kind:nn
          { \__tenkz_kernel_r_item:nn {#1} {kind} } {#1}
      }
  }
\cs_generate_variant:Nn \__tenkz_kernel_r_node:n { e, V }
\cs_new_protected:Npn \__tenkz_kernel_r_node_kind:nn #1#2
  {
    \str_case:nnF {#1}
      {
        {cell}
          {
            \__tenkz_geom_place_cell:nnee {#2} {kpic}
              { \__tenkz_kernel_r_item:nn {#2} {row} }
              { \__tenkz_kernel_r_item:nn {#2} {col} }
          }
        {member}
          {
            \__tenkz_kernel_basis_record:eeeN
              { \__tenkz_kernel_r_item:nn {#2} {row} }
              { \__tenkz_kernel_r_item:nn {#2} {col} }
              { \__tenkz_kernel_r_item:nn {#2} {member} }
              \l__tenkz_kernel_r_tl
            \quark_if_no_value:NF \l__tenkz_kernel_r_tl
              {
                \__tenkz_geom_place_rel:nVee {#2}
                  \l__tenkz_kernel_r_tl {0}{0}
              }
          }
        {rel}
          {
            \__tenkz_kernel_r_node:e { \__tenkz_kernel_r_item:nn {#2} {base} }
            \__tenkz_geom_place_rel:neee {#2}
              { \__tenkz_kernel_r_item:nn {#2} {base} }
              {
                \__tenkz_kernel_r_compass:n
                  { \__tenkz_kernel_r_item:nn {#2} {dir} }
              }
              { \__tenkz_kernel_r_item:nn {#2} {steps} }
          }
        {mid}
          {
            \__tenkz_kernel_r_node:e { \__tenkz_kernel_r_item:nn {#2} {first} }
            \__tenkz_kernel_r_node:e { \__tenkz_kernel_r_item:nn {#2} {second} }
            \__tenkz_geom_place_mid:nee {#2}
              { \__tenkz_kernel_r_item:nn {#2} {first} }
              { \__tenkz_kernel_r_item:nn {#2} {second} }
          }
        {record}
          {
            \prop_get:NeN \l__tenkz_kernel_named_prop
              { \__tenkz_kernel_r_item:nn {#2} {name} } \l__tenkz_kernel_r_tl
            \quark_if_no_value:NTF \l__tenkz_kernel_r_tl
              {
                \msg_error:nnee {tenkz}{kernel-render-crossref}
                  { \__tenkz_kernel_r_item:nn {#2} {text} } { }
              }
              {
                \__tenkz_geom_place_rel:nVee {#2}
                  \l__tenkz_kernel_r_tl {0} {0}
              }
          }
        {port}   { \__tenkz_kernel_r_port:n {#2} }
        {outside} { \__tenkz_kernel_r_outside:n {#2} }
        {crossing}
          {
            \__tenkz_kernel_r_opid:eN
              { \__tenkz_kernel_r_item:nn {#2} {first} }
              \l__tenkz_kernel_ow_a_tl
            \__tenkz_kernel_r_opid:eN
              { \__tenkz_kernel_r_item:nn {#2} {second} }
              \l__tenkz_kernel_ow_b_tl
            \exp_args:NnVV \__tenkz_kernel_r_crossing:nnn {#2}
              \l__tenkz_kernel_ow_a_tl \l__tenkz_kernel_ow_b_tl
          }
        {onwire} { \__tenkz_kernel_r_onwire:n {#2} }
      }
      { \msg_error:nnnn {tenkz}{kernel-render-todo} { #1 ~ address } {#2} }
  }

% ---------- a place on a curve --------------------------------------------------
% Two productions name a point of a wire rather than a place in the frame,
% and both answer through the wire's own route.  Their registers are their
% own: resolution below runs while the shared r_ scratch is mid-computation.
\tl_new:N \l__tenkz_kernel_ow_a_tl
\tl_new:N \l__tenkz_kernel_ow_b_tl
\tl_new:N \l__tenkz_kernel_ow_c_tl
\tl_new:N \l__tenkz_kernel_ow_t_tl
\tl_new:N \l__tenkz_kernel_ow_kind_tl
\tl_new:N \l__tenkz_kernel_ow_sid_tl
\seq_new:N \l__tenkz_kernel_ow_run_stack_seq

% The engine's name for one operand of a crossing.  A named wire answers with
% its name; a policy leg owns no record and answers with the id the leg pass
% saved its path under.
\cs_new_protected:Npn \__tenkz_kernel_r_opid:nN #1#2
  {
    \str_case:enF { \__tenkz_kernel_r_item:nn {#1} {kind} }
      {
        {leg}
          {
            \tl_set:Ne #2
              {
                leg - \__tenkz_kernel_r_item:nn {#1} {face}
                - \__tenkz_kernel_r_item:nn
                    { \__tenkz_kernel_r_item:nn {#1} {cell} } {row}
                - \__tenkz_kernel_r_item:nn
                    { \__tenkz_kernel_r_item:nn {#1} {cell} } {col}
              }
          }
      }
      { \tl_set:Ne #2 { \__tenkz_kernel_r_item:nn {#1} {name} } }
  }
\cs_generate_variant:Nn \__tenkz_kernel_r_opid:nN { eN }

% `on w t` is the point at fraction t of wire w.  A curve is measured along
% itself, so the engine answers the arclength question on the route it saved
% before any crossing cut it; that route is the one the wire inks, whether the
% author declared it or a closure policy generated it.  Only a wire that runs
% straight between two ends divides that run instead.  The tensor placed there
% is not a junction: the wire keeps its whole route and the place is read off
% it.
\cs_new_protected:Npn \__tenkz_kernel_r_port_open_geometry:n #1
  {
    \__tenkz_model_get:nnN {#1} {from} \l__tenkz_kernel_r_end_tl
    \__tenkz_kernel_r_wire_end_xy:VNN \l__tenkz_kernel_r_end_tl
      \l__tenkz_kernel_r_port_x_fp \l__tenkz_kernel_r_port_y_fp
    \__tenkz_model_get:nnN {#1} {host} \l__tenkz_kernel_port_record_tl
    \__tenkz_model_get:nnN {#1} {port-face}
      \l__tenkz_kernel_port_face_tl
    \exp_args:NVV \__tenkz_kernel_r_page_face:nnN
      \l__tenkz_kernel_port_record_tl
      \l__tenkz_kernel_port_face_tl
      \l__tenkz_kernel_r_port_turn_tl
    \__tenkz_model_get:nnN {#1} {port-type}
      \l__tenkz_kernel_port_type_tl
    \fp_set_eq:NN \l__tenkz_kernel_r_x_fp
      \l__tenkz_kernel_r_port_x_fp
    \fp_set_eq:NN \l__tenkz_kernel_r_y_fp
      \l__tenkz_kernel_r_port_y_fp
    \tl_set_eq:NN \l__tenkz_kernel_leg_dir_tl
      \l__tenkz_kernel_r_port_turn_tl
    \str_if_eq:VnTF \l__tenkz_kernel_port_type_tl {physical}
      {
        \fp_set:Nn \l__tenkz_kernel_r_reach_fp
          { \__tenkz_metric_ratio:n {physleg} }
        \str_case:VnF \l__tenkz_kernel_port_face_tl
          {
            {90}
              {
                \exp_args:NV \__tenkz_kernel_r_physical_enclosure:nn
                  \l__tenkz_kernel_port_record_tl {n}
              }
            {270}
              {
                \exp_args:NV \__tenkz_kernel_r_physical_enclosure:nn
                  \l__tenkz_kernel_port_record_tl {s}
              }
          }
          {
            \exp_args:NV \__tenkz_kernel_r_physical_enclosure:nn
              \l__tenkz_kernel_port_record_tl {other}
          }
      }
      {
        \fp_set:Nn \l__tenkz_kernel_r_reach_fp
          { \__tenkz_metric_ratio:n {stub} }
        % the region window that cuts an unbonded virtual index is the
        % innermost enclosure containing its host
        \exp_args:NV \__tenkz_kernel_r_virtual_enclosure:n
          \l__tenkz_kernel_port_record_tl
      }
    \__tenkz_kernel_r_sid:nN {#1} \l__tenkz_kernel_r_sid_tl
    \prop_get:NVN \l__tenkz_kernel_ro_legs_prop
      \l__tenkz_kernel_r_sid_tl \l__tenkz_kernel_leg_reach_tl
    \quark_if_no_value:NF \l__tenkz_kernel_leg_reach_tl
      {
        \clist_map_inline:Vn \l__tenkz_kernel_leg_reach_tl
          {
            \tl_set:Nn \l__tenkz_kernel_ro_row_tl {##1}
            \exp_last_unbraced:NV \__tenkz_kernel_r_leg_lane:nnn
              \l__tenkz_kernel_ro_row_tl
          }
      }
    \prop_get:NVN \l__tenkz_kernel_r_legref_prop
      \l__tenkz_kernel_r_sid_tl \l__tenkz_kernel_r_b_tl
    \quark_if_no_value:NF \l__tenkz_kernel_r_b_tl
      {
        \exp_args:NV \clist_map_inline:nn \l__tenkz_kernel_r_b_tl
          { \__tenkz_kernel_r_leg_reach:n {##1} }
      }
    \fp_set_eq:NN \l__tenkz_kernel_r_port_reach_fp
      \l__tenkz_kernel_r_reach_fp
    \fp_set:Nn \l__tenkz_kernel_r_port_tip_x_fp
      {
        \l__tenkz_kernel_r_port_x_fp
        + \l__tenkz_kernel_r_port_reach_fp
          * cosd( \l__tenkz_kernel_r_port_turn_tl )
      }
    \fp_set:Nn \l__tenkz_kernel_r_port_tip_y_fp
      {
        \l__tenkz_kernel_r_port_y_fp
        + \l__tenkz_kernel_r_port_reach_fp
          * sind( \l__tenkz_kernel_r_port_turn_tl )
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_r_onwire:n #1
  {
    \prop_get:NeN \l__tenkz_kernel_named_prop
      { \__tenkz_kernel_r_item:nn {#1} {wire} } \l__tenkz_kernel_ow_a_tl
    \tl_set:Ne \l__tenkz_kernel_ow_t_tl { \__tenkz_kernel_r_item:nn {#1} {t} }
    \quark_if_no_value:NTF \l__tenkz_kernel_ow_a_tl
      {
        \msg_error:nnee {tenkz}{kernel-render-onwire}
          { \__tenkz_kernel_r_item:nn {#1} {text} } { }
      }
      {
        \__tenkz_model_get:nnN { \tl_use:N \l__tenkz_kernel_ow_a_tl } {kind}
          \l__tenkz_kernel_ow_kind_tl
        \__tenkz_model_get:nnN
          { \tl_use:N \l__tenkz_kernel_ow_a_tl } {origin}
          \l__tenkz_kernel_ow_b_tl
        \str_if_eq:VnTF \l__tenkz_kernel_ow_b_tl {port-open}
          { \__tenkz_kernel_r_onwire_port_open:n {#1} }
          {
            \str_case:VnF \l__tenkz_kernel_ow_kind_tl
              {
                {string}  { \__tenkz_kernel_r_onwire_curve:n {#1} }
                {pairing} { \__tenkz_kernel_r_onwire_curve:n {#1} }
              }
              {
                % An index wire saves the path it inks under its own id, so a
                % generated cup or a trace closure answers along the arc the
                % reader sees rather than along the chord between its ends.
                % A leg with a free end has no second model end to divide, and
                % answers along its route for the same reason.
                \__tenkz_model_get:nnN
                  { \tl_use:N \l__tenkz_kernel_ow_a_tl } {from}
                  \l__tenkz_kernel_ow_b_tl
                \__tenkz_model_get:nnN
                  { \tl_use:N \l__tenkz_kernel_ow_a_tl } {to}
                  \l__tenkz_kernel_ow_c_tl
                \__tenkz_kernel_r_sid:nN
                  { \tl_use:N \l__tenkz_kernel_ow_a_tl }
                  \l__tenkz_kernel_ow_sid_tl
                \bool_lazy_and:nnTF
                  { ! \quark_if_no_value_p:N \l__tenkz_kernel_ow_b_tl }
                  { ! \quark_if_no_value_p:N \l__tenkz_kernel_ow_c_tl }
                  {
                    \__tenkz_string_bends:VTF \l__tenkz_kernel_ow_sid_tl
                      { \__tenkz_kernel_r_onwire_route:n {#1} }
                      {
                        % An arc route may not have saved its ink yet: the
                        % atom pass stands its beads before the wire pass
                        % runs.  The cubic the wire will draw is rebuilt
                        % from the same control frame instead of read back.
                        \__tenkz_model_get:nnN
                          { \tl_use:N \l__tenkz_kernel_ow_a_tl } {route}
                          \l__tenkz_kernel_ow_b_tl
                        \str_if_eq:VnTF \l__tenkz_kernel_ow_b_tl {arc}
                          { \__tenkz_kernel_r_onwire_arc:n {#1} }
                          { \__tenkz_kernel_r_onwire_run:n {#1} }
                      }
                  }
                  { \__tenkz_kernel_r_onwire_route:n {#1} }
              }
          }
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_r_onwire_port_open:n #1
  {
    % The port's host may itself be carried by another open port.  Preserve
    % this record and fraction across recursive host resolution just as a
    % straight on-wire run preserves both endpoints and its fraction.
    \seq_put_right:Ne \l__tenkz_kernel_ow_run_stack_seq
      { \exp_not:V \l__tenkz_kernel_ow_a_tl }
    \seq_put_right:Ne \l__tenkz_kernel_ow_run_stack_seq
      { \exp_not:V \l__tenkz_kernel_ow_t_tl }
    \exp_args:Ne \__tenkz_kernel_r_port_open_geometry:n
      { \seq_item:Nn \l__tenkz_kernel_ow_run_stack_seq {-2} }
    \__tenkz_geom_store:nnn {#1}
      {
        \l__tenkz_kernel_r_port_x_fp
        + \seq_item:Nn \l__tenkz_kernel_ow_run_stack_seq {-1}
          * ( \l__tenkz_kernel_r_port_tip_x_fp
              - \l__tenkz_kernel_r_port_x_fp )
      }
      {
        \l__tenkz_kernel_r_port_y_fp
        + \seq_item:Nn \l__tenkz_kernel_ow_run_stack_seq {-1}
          * ( \l__tenkz_kernel_r_port_tip_y_fp
              - \l__tenkz_kernel_r_port_y_fp )
      }
    \seq_pop_right:NN \l__tenkz_kernel_ow_run_stack_seq
      \l__tenkz_kernel_ow_t_tl
    \seq_pop_right:NN \l__tenkz_kernel_ow_run_stack_seq
      \l__tenkz_kernel_ow_a_tl
  }
% The point of a wire that its two model ends do not divide: a bending route,
% or a leg one of whose ends is a free tip the renderer chose.  Both are read
% off the route the wire inked.  A wire that inked no route at all keeps the
% engine's own diagnostic, which names the address the author wrote.
\cs_new_protected:Npn \__tenkz_kernel_r_onwire_route:n #1
  {
    \__tenkz_string_inked:VTF \l__tenkz_kernel_ow_sid_tl
      { \__tenkz_kernel_r_onwire_curve:n {#1} }
      {
        \msg_error:nnee {tenkz}{kernel-render-todo}
          { \__tenkz_kernel_r_item:nn {#1} {text} } {#1}
      }
  }
% the arclength point of a curve, converted once at the coordinate boundary
\cs_new_protected:Npn \__tenkz_kernel_r_onwire_curve:n #1
  {
    \__tenkz_kernel_r_sid:nN { \tl_use:N \l__tenkz_kernel_ow_a_tl }
      \l__tenkz_kernel_ow_sid_tl
    \exp_args:Nee \__tenkz_string_bead:nnn
      { \tl_use:N \l__tenkz_kernel_ow_sid_tl }
      { \tl_use:N \l__tenkz_kernel_ow_t_tl }
      { tenkz-on-#1 }
    \pgfpointanchor {tenkz-on-#1} {center}
    \__tenkz_geom_store:nnn {#1}
      {
        \dim_to_fp:n { \use:c {pgf@x} } / \dim_to_fp:n { \tenkz@pitch }
      }
      {
        \dim_to_fp:n { \use:c {pgf@y} } / \dim_to_fp:n { \tenkz@pitch }
      }
  }
% the two endpoint loaders share the renderer's own end reading
\cs_new_protected:Npn \__tenkz_kernel_r_wire_end_xy_a:n #1
  {
    \__tenkz_kernel_r_wire_end_xy:nNN {#1}
      \l__tenkz_kernel_r_x_fp \l__tenkz_kernel_r_y_fp
  }
\cs_new_protected:Npn \__tenkz_kernel_r_wire_end_xy_b:n #1
  {
    \__tenkz_kernel_r_wire_end_xy:nNN {#1}
      \l__tenkz_kernel_r_xb_fp \l__tenkz_kernel_r_yb_fp
  }
% the arc cubic's point at parameter #1, from the standing control frame
\cs_new_protected:Npn \__tenkz_kernel_r_arc_point:nNN #1#2#3
  {
    \fp_set:Nn #2
      {
        ( 1 - (#1) ) ^ 3 * \l__tenkz_kernel_r_x_fp
        + 3 * ( 1 - (#1) ) ^ 2 * (#1) * \l__tenkz_kernel_r_arc_cax_fp
        + 3 * ( 1 - (#1) ) * (#1) ^ 2 * \l__tenkz_kernel_r_arc_cbx_fp
        + (#1) ^ 3 * \l__tenkz_kernel_r_xb_fp
      }
    \fp_set:Nn #3
      {
        ( 1 - (#1) ) ^ 3 * \l__tenkz_kernel_r_y_fp
        + 3 * ( 1 - (#1) ) ^ 2 * (#1) * \l__tenkz_kernel_r_arc_cay_fp
        + 3 * ( 1 - (#1) ) * (#1) ^ 2 * \l__tenkz_kernel_r_arc_cby_fp
        + (#1) ^ 3 * \l__tenkz_kernel_r_yb_fp
      }
  }

% The same fraction of an arc route, answered from the model: the control
% frame the wire pass will draw is rebuilt here and the cubic divided at
% the asked-for parameter, so a bead stands on the drawn curve whether or
% not the ink exists yet.
\cs_new_protected:Npn \__tenkz_kernel_r_onwire_arc:n #1
  {
    \__tenkz_model_get:nnN { \tl_use:N \l__tenkz_kernel_ow_a_tl } {from}
      \l__tenkz_kernel_ow_b_tl
    \__tenkz_model_get:nnN { \tl_use:N \l__tenkz_kernel_ow_a_tl } {to}
      \l__tenkz_kernel_ow_c_tl
    % Resolving an end may recursively resolve an on-wire address, which
    % uses the ow_ registers: freeze the wire id, both ends, and the
    % fraction until both ends settle, as the straight run does.
    \seq_put_right:Ne \l__tenkz_kernel_ow_run_stack_seq
      { \exp_not:V \l__tenkz_kernel_ow_a_tl }
    \seq_put_right:Ne \l__tenkz_kernel_ow_run_stack_seq
      { \exp_not:V \l__tenkz_kernel_ow_b_tl }
    \seq_put_right:Ne \l__tenkz_kernel_ow_run_stack_seq
      { \exp_not:V \l__tenkz_kernel_ow_c_tl }
    \seq_put_right:Ne \l__tenkz_kernel_ow_run_stack_seq
      { \exp_not:V \l__tenkz_kernel_ow_t_tl }
    % the same endpoint reading the renderer uses: a port on a non-cell
    % atom stands at the measured silhouette hull, not at its generic
    % coordinates, and the provisional cubic must match the drawn one
    \exp_args:Ne \__tenkz_kernel_r_wire_end_xy_a:n
      { \seq_item:Nn \l__tenkz_kernel_ow_run_stack_seq {-3} }
    \exp_args:Ne \__tenkz_kernel_r_wire_end_xy_b:n
      { \seq_item:Nn \l__tenkz_kernel_ow_run_stack_seq {-2} }
    \exp_args:Ne \__tenkz_kernel_r_arc_controls:n
      { \seq_item:Nn \l__tenkz_kernel_ow_run_stack_seq {-4} }
    \bool_if:NTF \l__tenkz_kernel_r_arc_wrap_bool
      {
        % A wrap's beads divide the whole drawn route, fold and run and
        % fold, by arc length -- the same contract every other route
        % keeps.  Both folds' control frames are built before any walk
        % (the walk spends the angle registers), each fold's length is
        % walked in its own frame -- only the middle run's length comes
        % in closed form -- and the target settles into whichever
        % stretch holds its share.
        \fp_set_eq:NN \l__tenkz_kernel_r_arc_ex_fp \l__tenkz_kernel_r_xb_fp
        \fp_set_eq:NN \l__tenkz_kernel_r_arc_ey_fp \l__tenkz_kernel_r_yb_fp
        \fp_set:Nn \l__tenkz_kernel_r_arc_gax_fp
          {
            \l__tenkz_kernel_r_arc_nx_fp + \l__tenkz_kernel_r_arc_dtwo_fp
              * cosd ( \l__tenkz_kernel_r_arc_a_fp )
          }
        \fp_set:Nn \l__tenkz_kernel_r_arc_gay_fp
          {
            \l__tenkz_kernel_r_arc_ny_fp + \l__tenkz_kernel_r_arc_dtwo_fp
              * sind ( \l__tenkz_kernel_r_arc_a_fp )
          }
        \fp_set:Nn \l__tenkz_kernel_r_arc_gbx_fp
          {
            \l__tenkz_kernel_r_arc_ex_fp + \l__tenkz_kernel_r_arc_dtwo_fp
              * cosd ( \l__tenkz_kernel_r_arc_ab_fp + 180 )
          }
        \fp_set:Nn \l__tenkz_kernel_r_arc_gby_fp
          {
            \l__tenkz_kernel_r_arc_ey_fp + \l__tenkz_kernel_r_arc_dtwo_fp
              * sind ( \l__tenkz_kernel_r_arc_ab_fp + 180 )
          }
        % fold one's frame: P to M, controls off the from face and back
        % along the chord bearing, at fold one's own closure distance
        \fp_set:Nn \l__tenkz_kernel_r_arc_cax_fp
          {
            \l__tenkz_kernel_r_x_fp + \l__tenkz_kernel_r_arc_done_fp
              * cosd ( \l__tenkz_kernel_r_arc_af_fp )
          }
        \fp_set:Nn \l__tenkz_kernel_r_arc_cay_fp
          {
            \l__tenkz_kernel_r_y_fp + \l__tenkz_kernel_r_arc_done_fp
              * sind ( \l__tenkz_kernel_r_arc_af_fp )
          }
        \fp_set:Nn \l__tenkz_kernel_r_arc_cbx_fp
          {
            \l__tenkz_kernel_r_arc_mx_fp - \l__tenkz_kernel_r_arc_done_fp
              * cosd ( \l__tenkz_kernel_r_arc_a_fp )
          }
        \fp_set:Nn \l__tenkz_kernel_r_arc_cby_fp
          {
            \l__tenkz_kernel_r_arc_my_fp - \l__tenkz_kernel_r_arc_done_fp
              * sind ( \l__tenkz_kernel_r_arc_a_fp )
          }
        % the from end survives the walks in its own registers, because
        % each fold's frame is loaded into the shared cubic registers
        \fp_set_eq:NN \l__tenkz_kernel_r_arc_hx_fp \l__tenkz_kernel_r_x_fp
        \fp_set_eq:NN \l__tenkz_kernel_r_arc_hy_fp \l__tenkz_kernel_r_y_fp
        \fp_set_eq:NN \l__tenkz_kernel_r_xb_fp \l__tenkz_kernel_r_arc_mx_fp
        \fp_set_eq:NN \l__tenkz_kernel_r_yb_fp \l__tenkz_kernel_r_arc_my_fp
        \__tenkz_kernel_r_onwire_arc_walk:
        \fp_set_eq:NN \l__tenkz_kernel_r_arc_lone_fp
          \l__tenkz_kernel_r_arc_len_fp
        \fp_set:Nn \l__tenkz_kernel_r_arc_ltwo_fp
          {
            sqrt (
              ( \l__tenkz_kernel_r_arc_nx_fp
                - \l__tenkz_kernel_r_arc_mx_fp ) ^ 2 +
              ( \l__tenkz_kernel_r_arc_ny_fp
                - \l__tenkz_kernel_r_arc_my_fp ) ^ 2 )
          }
        % the second fold is not the first mirrored: the predicate admits
        % faces at unequal turns, so its length is walked, not reused
        \fp_set_eq:NN \l__tenkz_kernel_r_x_fp \l__tenkz_kernel_r_arc_nx_fp
        \fp_set_eq:NN \l__tenkz_kernel_r_y_fp \l__tenkz_kernel_r_arc_ny_fp
        \fp_set_eq:NN \l__tenkz_kernel_r_xb_fp \l__tenkz_kernel_r_arc_ex_fp
        \fp_set_eq:NN \l__tenkz_kernel_r_yb_fp \l__tenkz_kernel_r_arc_ey_fp
        \fp_set_eq:NN \l__tenkz_kernel_r_arc_cax_fp
          \l__tenkz_kernel_r_arc_gax_fp
        \fp_set_eq:NN \l__tenkz_kernel_r_arc_cay_fp
          \l__tenkz_kernel_r_arc_gay_fp
        \fp_set_eq:NN \l__tenkz_kernel_r_arc_cbx_fp
          \l__tenkz_kernel_r_arc_gbx_fp
        \fp_set_eq:NN \l__tenkz_kernel_r_arc_cby_fp
          \l__tenkz_kernel_r_arc_gby_fp
        \__tenkz_kernel_r_onwire_arc_walk:
        \fp_set_eq:NN \l__tenkz_kernel_r_arc_lthree_fp
          \l__tenkz_kernel_r_arc_len_fp
        \fp_set:Nn \l__tenkz_kernel_r_arc_tgt_fp
          {
            ( \seq_item:Nn \l__tenkz_kernel_ow_run_stack_seq {-1} )
            * ( \l__tenkz_kernel_r_arc_lone_fp
                + \l__tenkz_kernel_r_arc_ltwo_fp
                + \l__tenkz_kernel_r_arc_lthree_fp )
          }
        \fp_compare:nNnTF
          { \l__tenkz_kernel_r_arc_tgt_fp } >
          {
            \l__tenkz_kernel_r_arc_lone_fp
            + \l__tenkz_kernel_r_arc_ltwo_fp
          }
          {
            % the second fold's frame and cumulative table are current
            \fp_set:Nn \l__tenkz_kernel_r_arc_tgt_fp
              {
                \l__tenkz_kernel_r_arc_tgt_fp
                - \l__tenkz_kernel_r_arc_lone_fp
                - \l__tenkz_kernel_r_arc_ltwo_fp
              }
            \__tenkz_kernel_r_onwire_arc_locate:nn {#1}
              {
                \l__tenkz_kernel_r_arc_lthree_fp > 0
                  ? \l__tenkz_kernel_r_arc_tgt_fp
                    / \l__tenkz_kernel_r_arc_lthree_fp
                  : 1
              }
          }
          {
            \fp_compare:nNnTF
              { \l__tenkz_kernel_r_arc_tgt_fp } >
              { \l__tenkz_kernel_r_arc_lone_fp }
              {
                % the middle run
                \fp_set:Nn \l__tenkz_kernel_r_arc_t_fp
                  {
                    \l__tenkz_kernel_r_arc_ltwo_fp > 0
                      ? ( \l__tenkz_kernel_r_arc_tgt_fp
                          - \l__tenkz_kernel_r_arc_lone_fp )
                        / \l__tenkz_kernel_r_arc_ltwo_fp
                      : 0
                  }
                \__tenkz_geom_store:nnn {#1}
                  {
                    \l__tenkz_kernel_r_arc_mx_fp
                    + \l__tenkz_kernel_r_arc_t_fp
                      * ( \l__tenkz_kernel_r_arc_nx_fp
                          - \l__tenkz_kernel_r_arc_mx_fp )
                  }
                  {
                    \l__tenkz_kernel_r_arc_my_fp
                    + \l__tenkz_kernel_r_arc_t_fp
                      * ( \l__tenkz_kernel_r_arc_ny_fp
                          - \l__tenkz_kernel_r_arc_my_fp )
                  }
              }
              {
                % fold one again: its frame and table were spent by the
                % second fold's walk, so both are rebuilt
                \fp_set_eq:NN \l__tenkz_kernel_r_x_fp
                  \l__tenkz_kernel_r_arc_hx_fp
                \fp_set_eq:NN \l__tenkz_kernel_r_y_fp
                  \l__tenkz_kernel_r_arc_hy_fp
                \fp_set_eq:NN \l__tenkz_kernel_r_xb_fp
                  \l__tenkz_kernel_r_arc_mx_fp
                \fp_set_eq:NN \l__tenkz_kernel_r_yb_fp
                  \l__tenkz_kernel_r_arc_my_fp
                \fp_set:Nn \l__tenkz_kernel_r_arc_cax_fp
                  {
                    \l__tenkz_kernel_r_arc_hx_fp
                    + \l__tenkz_kernel_r_arc_done_fp
                      * cosd ( \l__tenkz_kernel_r_arc_tf_fp )
                  }
                \fp_set:Nn \l__tenkz_kernel_r_arc_cay_fp
                  {
                    \l__tenkz_kernel_r_arc_hy_fp
                    + \l__tenkz_kernel_r_arc_done_fp
                      * sind ( \l__tenkz_kernel_r_arc_tf_fp )
                  }
                \fp_set:Nn \l__tenkz_kernel_r_arc_cbx_fp
                  {
                    \l__tenkz_kernel_r_arc_mx_fp
                    - \l__tenkz_kernel_r_arc_done_fp
                      * cosd ( \l__tenkz_kernel_r_arc_a_fp )
                  }
                \fp_set:Nn \l__tenkz_kernel_r_arc_cby_fp
                  {
                    \l__tenkz_kernel_r_arc_my_fp
                    - \l__tenkz_kernel_r_arc_done_fp
                      * sind ( \l__tenkz_kernel_r_arc_a_fp )
                  }
                \__tenkz_kernel_r_onwire_arc_measure:nn {#1}
                  {
                    \l__tenkz_kernel_r_arc_lone_fp > 0
                      ? \l__tenkz_kernel_r_arc_tgt_fp
                        / \l__tenkz_kernel_r_arc_lone_fp
                      : 0
                  }
              }
          }
      }
      {
        \__tenkz_kernel_r_onwire_arc_measure:nn {#1}
          { \seq_item:Nn \l__tenkz_kernel_ow_run_stack_seq {-1} }
      }
    \seq_pop_right:NN \l__tenkz_kernel_ow_run_stack_seq
      \l__tenkz_kernel_ow_t_tl
    \seq_pop_right:NN \l__tenkz_kernel_ow_run_stack_seq
      \l__tenkz_kernel_ow_c_tl
    \seq_pop_right:NN \l__tenkz_kernel_ow_run_stack_seq
      \l__tenkz_kernel_ow_b_tl
    \seq_pop_right:NN \l__tenkz_kernel_ow_run_stack_seq
      \l__tenkz_kernel_ow_a_tl
  }
% `on w t' asks for an arclength fraction, not the cubic's parameter:
% walk the cubic in thirty-two chords, find where the asked-for share of
% the total length falls, and read the parameter off that chord.  The
% chord walk underestimates each sub-arc alike, so the found parameter is
% exact at both ends and off by under a tenth of a percent of the length
% between them at drawing curvatures -- well under a point at any pitch a
% page carries.
\cs_new_protected:Npn \__tenkz_kernel_r_onwire_arc_measure:nn #1#2
  {
    \__tenkz_kernel_r_onwire_arc_walk:
    \__tenkz_kernel_r_onwire_arc_locate:nn {#1} {#2}
  }
% the length walk: thirty-two sub-chords of the standing cubic frame into
% the running length and its cumulative table
\cs_new_protected:Npn \__tenkz_kernel_r_onwire_arc_walk:
  {
    \fp_zero:N \l__tenkz_kernel_r_arc_len_fp
    \seq_clear:N \l__tenkz_kernel_r_arc_cum_seq
    \fp_set_eq:NN \l__tenkz_kernel_r_arc_px_fp \l__tenkz_kernel_r_x_fp
    \fp_set_eq:NN \l__tenkz_kernel_r_arc_py_fp \l__tenkz_kernel_r_y_fp
    \int_step_inline:nn {32}
      {
        \__tenkz_kernel_r_arc_point:nNN { ##1 / 32 }
          \l__tenkz_kernel_r_arc_af_fp \l__tenkz_kernel_r_arc_ab_fp
        \fp_add:Nn \l__tenkz_kernel_r_arc_len_fp
          {
            sqrt (
              ( \l__tenkz_kernel_r_arc_af_fp
                - \l__tenkz_kernel_r_arc_px_fp ) ^ 2 +
              ( \l__tenkz_kernel_r_arc_ab_fp
                - \l__tenkz_kernel_r_arc_py_fp ) ^ 2 )
          }
        \seq_put_right:Ne \l__tenkz_kernel_r_arc_cum_seq
          { \fp_use:N \l__tenkz_kernel_r_arc_len_fp }
        \fp_set_eq:NN \l__tenkz_kernel_r_arc_px_fp
          \l__tenkz_kernel_r_arc_af_fp
        \fp_set_eq:NN \l__tenkz_kernel_r_arc_py_fp
          \l__tenkz_kernel_r_arc_ab_fp
      }
  }
% the locate half: place the asked-for share of the walked length and
% store the cubic's point there under the geometry id
\cs_new_protected:Npn \__tenkz_kernel_r_onwire_arc_locate:nn #1#2
  {
    \fp_set:Nn \l__tenkz_kernel_r_arc_tgt_fp
      { (#2) * \l__tenkz_kernel_r_arc_len_fp }
    \fp_set:Nn \l__tenkz_kernel_r_arc_t_fp {1}
    \fp_zero:N \l__tenkz_kernel_r_arc_px_fp
    \int_step_inline:nn {32}
      {
        \fp_set:Nn \l__tenkz_kernel_r_arc_py_fp
          { \seq_item:Nn \l__tenkz_kernel_r_arc_cum_seq {##1} }
        \fp_compare:nNnT
          { \l__tenkz_kernel_r_arc_t_fp } = {1}
          {
            \fp_compare:nNnT
              { \l__tenkz_kernel_r_arc_py_fp } >
              { \l__tenkz_kernel_r_arc_tgt_fp }
              {
                \fp_set:Nn \l__tenkz_kernel_r_arc_t_fp
                  {
                    ( ##1 - 1
                      + ( \l__tenkz_kernel_r_arc_tgt_fp
                          - \l__tenkz_kernel_r_arc_px_fp )
                        / ( \l__tenkz_kernel_r_arc_py_fp
                            - \l__tenkz_kernel_r_arc_px_fp ) ) / 32
                  }
              }
          }
        \fp_set_eq:NN \l__tenkz_kernel_r_arc_px_fp
          \l__tenkz_kernel_r_arc_py_fp
      }
    \__tenkz_kernel_r_arc_point:nNN { \l__tenkz_kernel_r_arc_t_fp }
      \l__tenkz_kernel_r_arc_af_fp \l__tenkz_kernel_r_arc_ab_fp
    \__tenkz_geom_store:nnn {#1}
      { \l__tenkz_kernel_r_arc_af_fp }
      { \l__tenkz_kernel_r_arc_ab_fp }
  }

% the same fraction of a wire that runs straight between its two ends
\cs_new_protected:Npn \__tenkz_kernel_r_onwire_run:n #1
  {
    \__tenkz_model_get:nnN { \tl_use:N \l__tenkz_kernel_ow_a_tl } {from}
      \l__tenkz_kernel_ow_b_tl
    \__tenkz_model_get:nnN { \tl_use:N \l__tenkz_kernel_ow_a_tl } {to}
      \l__tenkz_kernel_ow_c_tl
    \bool_lazy_or:nnTF
      { \quark_if_no_value_p:N \l__tenkz_kernel_ow_b_tl }
      { \quark_if_no_value_p:N \l__tenkz_kernel_ow_c_tl }
      {
        \msg_error:nnee {tenkz}{kernel-render-todo}
          { \__tenkz_kernel_r_item:nn {#1} {text} } {#1}
      }
      {
        % Resolving an end may recursively resolve an on-wire or crossing
        % address, both of which use the ow_ registers.  Freeze this run's
        % two node ids and fraction on a LIFO stack until both ends settle.
        \seq_put_right:Ne \l__tenkz_kernel_ow_run_stack_seq
          { \exp_not:V \l__tenkz_kernel_ow_b_tl }
        \seq_put_right:Ne \l__tenkz_kernel_ow_run_stack_seq
          { \exp_not:V \l__tenkz_kernel_ow_c_tl }
        \seq_put_right:Ne \l__tenkz_kernel_ow_run_stack_seq
          { \exp_not:V \l__tenkz_kernel_ow_t_tl }
        \__tenkz_kernel_r_node:e
          { \seq_item:Nn \l__tenkz_kernel_ow_run_stack_seq {-3} }
        \__tenkz_kernel_r_node:e
          { \seq_item:Nn \l__tenkz_kernel_ow_run_stack_seq {-2} }
        \use:e
          {
            \__tenkz_geom_place_onseg:nnnn {#1}
              { \seq_item:Nn \l__tenkz_kernel_ow_run_stack_seq {-3} }
              { \seq_item:Nn \l__tenkz_kernel_ow_run_stack_seq {-2} }
              { \seq_item:Nn \l__tenkz_kernel_ow_run_stack_seq {-1} }
          }
        \seq_pop_right:NN \l__tenkz_kernel_ow_run_stack_seq
          \l__tenkz_kernel_ow_t_tl
        \seq_pop_right:NN \l__tenkz_kernel_ow_run_stack_seq
          \l__tenkz_kernel_ow_c_tl
        \seq_pop_right:NN \l__tenkz_kernel_ow_run_stack_seq
          \l__tenkz_kernel_ow_b_tl
      }
  }

% ---------- the selector and the offset hull -----------------------------------
% A selector names a SET of atom records: an address, a braced list of
% addresses, a cell range, or the whole picture.  Named cluster carriers
% expand to their generated members.  What the model keeps for enclosure
% marks is the resolved membership -- canonical record ids -- and never the
% expression that produced it; route selectors retain their author spelling
% until their own model-normalization landing.
\seq_new:N \l__tenkz_kernel_sel_seq
\seq_new:N \l__tenkz_kernel_sel_work_seq
\seq_new:N \l__tenkz_kernel_sel_obst_seq
\seq_new:N \l__tenkz_kernel_leg_sel_seq
\seq_new:N \l__tenkz_kernel_leg_obst_seq
\tl_new:N \l__tenkz_kernel_leg_tl
\tl_new:N \l__tenkz_kernel_leg_reach_tl
\tl_new:N \l__tenkz_kernel_leg_dir_tl
\tl_new:N \l__tenkz_kernel_leg_gx_tl
\tl_new:N \l__tenkz_kernel_leg_gy_tl
% Containment depth of the enclosure a leg or stub is measured against, and
% the clearance count past the member supports it must reach.  A physical
% ray pierces the contour by one clearance (pad two: one to the contour,
% one through it); a virtual stub cut by a region window ends on the
% contour itself (pad one).
\tl_new:N \l__tenkz_kernel_leg_depth_tl
\tl_set:Nn \l__tenkz_kernel_leg_depth_tl {0}
\tl_new:N \l__tenkz_kernel_leg_pad_tl
\tl_set:Nn \l__tenkz_kernel_leg_pad_tl {2}
% A leg asks every contour whether it holds the site the leg grew from, and
% that question resolves a range through the shared cell registers.  The leg
% therefore keeps its own cell in registers of its own: read from the shared
% pair afterwards, the leg of the second site of an enclosed range answers
% with the first site's address, and two legs then claim one name.
\tl_new:N \l__tenkz_kernel_leg_row_tl
\tl_new:N \l__tenkz_kernel_leg_col_tl
% The canonical name of a policy leg: its face and the cell it grows from.
% One spelling serves the record, the engine path, and the address, so a leg
% is reached by the same word wherever it is spoken about.
\tl_new:N \l__tenkz_kernel_leg_name_tl
% The hull builder runs INSIDE the leg walk, which is mid-computation on its
% own position and its own cell.  It therefore touches none of the shared
% scratch: these are its alone.
\fp_new:N \l__tenkz_kernel_hull_x_fp
\fp_new:N \l__tenkz_kernel_hull_y_fp
\tl_new:N \l__tenkz_kernel_hull_a_tl
\tl_new:N \l__tenkz_kernel_hull_b_tl
\tl_new:N \l__tenkz_kernel_hull_skin_tl
\tl_new:N \l__tenkz_kernel_hull_size_tl
\tl_new:N \l__tenkz_kernel_hull_radius_tl
\tl_new:N \l__tenkz_kernel_hull_turn_tl
\tl_new:N \l__tenkz_kernel_hull_measure_name_tl
\tl_new:N \l__tenkz_kernel_hull_shape_tl
\tl_new:N \l__tenkz_kernel_hull_box_tl
\tl_new:N \l__tenkz_kernel_hull_xmin_tl
\tl_new:N \l__tenkz_kernel_hull_xmax_tl
\tl_new:N \l__tenkz_kernel_hull_ymin_tl
\tl_new:N \l__tenkz_kernel_hull_ymax_tl
\bool_new:N \l__tenkz_kernel_hull_measure_valid_bool
\tl_new:N \l__tenkz_kernel_sel_tl
\bool_new:N \l__tenkz_kernel_sel_bool
\bool_new:N \l__tenkz_kernel_sel_valid_bool
\int_new:N \l__tenkz_kernel_hull_box_int
\prop_new:N \l__tenkz_kernel_hull_live_prop
\prop_new:N \l__tenkz_kernel_hull_measured_prop
\prop_new:N \l__tenkz_kernel_glyph_face_probe_prop
\dim_new:N \l__tenkz_kernel_hull_west_dim
\dim_new:N \l__tenkz_kernel_hull_east_dim
\dim_new:N \l__tenkz_kernel_hull_south_dim
\dim_new:N \l__tenkz_kernel_hull_north_dim
\dim_new:N \l__tenkz_kernel_hull_xone_dim
\dim_new:N \l__tenkz_kernel_hull_yone_dim
\dim_new:N \l__tenkz_kernel_hull_xtwo_dim
\dim_new:N \l__tenkz_kernel_hull_ytwo_dim
\dim_new:N \l__tenkz_kernel_hull_xthree_dim
\dim_new:N \l__tenkz_kernel_hull_ythree_dim
\tl_new:N \l__tenkz_kernel_hull_natural_name_tl
\dim_new:N \l__tenkz_kernel_hull_natural_wd_dim
\dim_new:N \l__tenkz_kernel_hull_natural_ht_dim
\dim_new:N \l__tenkz_kernel_hull_floor_wd_dim
\dim_new:N \l__tenkz_kernel_hull_floor_ht_dim
\fp_new:N \l__tenkz_kernel_hull_xoff_fp
\fp_new:N \l__tenkz_kernel_hull_yoff_fp
\fp_new:N \l__tenkz_kernel_hull_xrot_fp
\fp_new:N \l__tenkz_kernel_hull_yrot_fp
\tl_new:N \l__tenkz_kernel_sel_row_a_tl
\tl_new:N \l__tenkz_kernel_sel_col_a_tl
\tl_new:N \l__tenkz_kernel_sel_row_b_tl
\tl_new:N \l__tenkz_kernel_sel_col_b_tl
\msg_new:nnn {tenkz}{kernel-selector-empty}
  { [TKZ-KERNEL-SELECTOR-EMPTY]~the~selection~'#1'~names~no~record }
\msg_new:nnn {tenkz}{kernel-selector-kind}
  { [TKZ-KERNEL-SELECTOR-KIND]~the~selection~'#1'~does~not~resolve~
    only~to~atoms~or~a~range~of~cells }

% selector text #1 -> the record ids it names, in #2.
%
% A selector may subtract: `A - B - C' names what A names and neither B nor
% C does, and the subtractions apply left to right.  Each term is a selector
% in its own right, so either side of the operator may be a braced union, a
% cell range, or a name.  The resolved membership is a set of record ids, so
% the difference is the set difference on those ids and nothing else -- what
% the model keeps is still the members, never the expression.
%
% The operator is spelled with a space on each side.  Addresses carry hyphens
% of their own -- a cluster member reads `a-2-2' -- and a spaced hyphen is the
% one place in the grammar where a hyphen separates operands.
\seq_new:N \l__tenkz_kernel_sel_terms_seq
\seq_new:N \l__tenkz_kernel_sel_diff_seq
\tl_new:N \l__tenkz_kernel_sel_expr_tl
\tl_new:N \l__tenkz_kernel_sel_term_tl
\cs_new_protected:Npn \__tenkz_kernel_select:nN #1#2
  {
    \seq_clear:N #2
    \seq_clear:N \l__tenkz_kernel_sel_work_seq
    \bool_set_true:N \l__tenkz_kernel_sel_valid_bool
    \tl_set:Nn \l__tenkz_kernel_sel_expr_tl {#1}
    \tl_if_in:NnTF \l__tenkz_kernel_sel_expr_tl { ~-~ }
      {
        \__tenkz_kernel_select_split:N \l__tenkz_kernel_sel_expr_tl
        \seq_pop_left:NN \l__tenkz_kernel_sel_terms_seq
          \l__tenkz_kernel_sel_term_tl
        \exp_args:NV \__tenkz_kernel_select_term:nN
          \l__tenkz_kernel_sel_term_tl \l__tenkz_kernel_sel_work_seq
        \seq_map_inline:Nn \l__tenkz_kernel_sel_terms_seq
          { \__tenkz_kernel_select_subtract:n {##1} }
      }
      { \__tenkz_kernel_select_term:nN {#1} \l__tenkz_kernel_sel_work_seq }
    \bool_if:NT \l__tenkz_kernel_sel_valid_bool
      {
        \seq_if_empty:NT \l__tenkz_kernel_sel_work_seq
          {
            \bool_set_false:N \l__tenkz_kernel_sel_valid_bool
            \msg_error:nnn {tenkz}{kernel-selector-empty} {#1}
          }
      }
    \bool_if:NTF \l__tenkz_kernel_sel_valid_bool
      {
        \__tenkz_model_canonicalize_ids:NN
          \l__tenkz_kernel_sel_work_seq #2
      }
      { \seq_clear:N #2 }
  }
\cs_generate_variant:Nn \__tenkz_kernel_select:nN { VN }

% The terms of a difference, in order.  A term that is one brace group must
% keep it -- a braced union is exactly that -- so the scan carries a mark
% ahead of each term and strips it afterwards, rather than letting the
% delimited match remove the group.
\tl_new:N \l__tenkz_kernel_sel_scan_tl
\cs_new_protected:Npn \__tenkz_kernel_select_split:N #1
  {
    \seq_clear:N \l__tenkz_kernel_sel_terms_seq
    \tl_set_eq:NN \l__tenkz_kernel_sel_scan_tl #1
    \__tenkz_kernel_select_split_loop:
  }
\cs_new_protected:Npn \__tenkz_kernel_select_split_loop:
  {
    \tl_if_in:NnTF \l__tenkz_kernel_sel_scan_tl { ~-~ }
      {
        \exp_after:wN \__tenkz_kernel_select_split:w
          \exp_after:wN \q_mark \l__tenkz_kernel_sel_scan_tl \q_stop
        \__tenkz_kernel_select_split_loop:
      }
      {
        \seq_put_right:NV \l__tenkz_kernel_sel_terms_seq
          \l__tenkz_kernel_sel_scan_tl
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_select_split:w #1 ~-~ #2 \q_stop
  {
    \tl_set:No \l__tenkz_kernel_sel_term_tl { \use_none:n #1 }
    \seq_put_right:NV \l__tenkz_kernel_sel_terms_seq
      \l__tenkz_kernel_sel_term_tl
    \tl_set:Nn \l__tenkz_kernel_sel_scan_tl {#2}
  }

% one subtracted term: the records it names leave the running membership
\cs_new_protected:Npn \__tenkz_kernel_select_subtract:n #1
  {
    \seq_clear:N \l__tenkz_kernel_sel_diff_seq
    \__tenkz_kernel_select_term:nN {#1} \l__tenkz_kernel_sel_diff_seq
    \seq_map_inline:Nn \l__tenkz_kernel_sel_diff_seq
      { \seq_remove_all:Nn \l__tenkz_kernel_sel_work_seq {##1} }
  }

% one term of a selector: the whole picture, a braced union, or one address
\cs_new_protected:Npn \__tenkz_kernel_select_term:nN #1#2
  {
    \tl_set:Nn \l__tenkz_kernel_sel_tl {#1}
    \tl_trim_spaces:N \l__tenkz_kernel_sel_tl
    \str_if_eq:VnTF \l__tenkz_kernel_sel_tl {picture}
      {
        % The whole picture is a selection like any other.  This is why the
        % corridor's own word retires: a wire that travels around everything
        % selects everything, and needs no second spelling to say so.
        \__tenkz_model_map_ids:nn {atom}
          {
            \__tenkz_kernel_select_record:nnN
              {##1} {picture} #2
          }
      }
      {
    % A braced list is one group and nothing else.  A range whose ends
    % arrived through a key value also begins with a group -- the cell
    % coordinates are braced against the comma that separates keys -- so
    % leading a group is not enough to tell the two apart.
    \bool_set_false:N \l__tenkz_kernel_sel_bool
    \tl_if_single:NT \l__tenkz_kernel_sel_tl
      {
        \exp_args:NV \tl_if_head_is_group:nT \l__tenkz_kernel_sel_tl
          { \bool_set_true:N \l__tenkz_kernel_sel_bool }
      }
    \bool_if:NTF \l__tenkz_kernel_sel_bool
      {
        % a braced list: every member is an address in its own right
        \tl_set:Ne \l__tenkz_kernel_sel_tl
          { \exp_args:NV \tl_head:n \l__tenkz_kernel_sel_tl }
        \regex_replace_all:nnN
          { \( \s* (\d+) \s* , \s* (\d+) \s* , \s* (\d+) \s* \) }
          { \cB\{ \( \1 , \2 , \3 \) \cE\} }
          \l__tenkz_kernel_sel_tl
        \regex_replace_all:nnN
          { \( \s* (\d+) \s* , \s* (\d+) \s* \) }
          { \cB\{ \( \1 , \2 \) \cE\} }
          \l__tenkz_kernel_sel_tl
        \exp_args:NV \clist_map_inline:nn \l__tenkz_kernel_sel_tl
          { \__tenkz_kernel_select_addr:nN {##1} #2 }
      }
      {
        \exp_args:NV \__tenkz_kernel_select_addr:nN
          \l__tenkz_kernel_sel_tl #2
      }
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_mark_members:nN #1#2
  {
    \__tenkz_model_get:nnN {#1} {members} \l__tenkz_kernel_sel_tl
    \quark_if_no_value:NTF \l__tenkz_kernel_sel_tl
      { \seq_clear:N #2 }
      { \exp_args:NNV \seq_set_from_clist:Nn #2 \l__tenkz_kernel_sel_tl }
  }

% one address of a selector: a range spreads over the block it corners,
% anything else names the single record it resolves to
\cs_new_protected:Npn \__tenkz_kernel_select_addr:nN #1#2
  {
    \__tenkz_kernel_addr:nN {#1} \l__tenkz_kernel_scratch_tl
    \str_if_eq:eeTF
      { \__tenkz_kernel_node_item:Nn \l__tenkz_kernel_scratch_tl {kind} }
      {range}
      { \exp_args:NV \__tenkz_kernel_select_range:nN \l__tenkz_kernel_scratch_tl #2 }
      { \__tenkz_kernel_select_one:nN {#1} #2 }
  }
\cs_generate_variant:Nn \__tenkz_kernel_select_addr:nN { VN }

% The atom occupying a cell or carrying a name.  A cluster carrier is a
% semantic group: selecting its name selects its generated member atoms.
\cs_new_protected:Npn \__tenkz_kernel_select_one:nN #1#2
  {
    \tl_set:Nn \l_tmpa_tl {#1}
    \tl_trim_spaces:N \l_tmpa_tl
    \prop_get:NVN \l__tenkz_kernel_named_prop \l_tmpa_tl \l_tmpb_tl
    \quark_if_no_value:NTF \l_tmpb_tl
      {
        \exp_args:NV \__tenkz_kernel_addr:nN \l_tmpa_tl \l_tmpb_tl
        \str_case:enF
          { \__tenkz_kernel_node_item:Nn \l_tmpb_tl {kind} }
          {
            {cell}
              {
                \exp_args:Nee \__tenkz_kernel_select_cell_records:nnN
                  { \__tenkz_kernel_node_item:Nn \l_tmpb_tl {row} }
                  { \__tenkz_kernel_node_item:Nn \l_tmpb_tl {col} }
                  #2
              }
            {member}
              {
                \__tenkz_kernel_basis_record:eeeN
                  { \__tenkz_kernel_node_item:Nn \l_tmpb_tl {row} }
                  { \__tenkz_kernel_node_item:Nn \l_tmpb_tl {col} }
                  { \__tenkz_kernel_node_item:Nn \l_tmpb_tl {member} }
                  \l_tmpb_tl
                \quark_if_no_value:NTF \l_tmpb_tl
                  { \__tenkz_kernel_selector_reject:n {#1} }
                  { \__tenkz_kernel_select_record:VnN \l_tmpb_tl {#1} #2 }
              }
            {record}
              { \__tenkz_kernel_selector_reject:n {#1} }
          }
          { \__tenkz_kernel_selector_reject:n {#1} }
      }
      { \__tenkz_kernel_select_record:VnN \l_tmpb_tl {#1} #2 }
  }

\cs_new_protected:Npn \__tenkz_kernel_select_record:nnN #1#2#3
  {
    \prop_get:NnN \l__tenkz_model_class_prop {#1} \l_tmpa_tl
    \str_if_eq:VnTF \l_tmpa_tl {atom}
      {
        \__tenkz_model_get:nnN {#1} {cluster} \l_tmpa_tl
        \quark_if_no_value:NTF \l_tmpa_tl
          { \seq_put_right:Nn #3 {#1} }
          {
            \__tenkz_model_get:nnN {#1} {name} \l_tmpa_tl
            \__tenkz_model_map_ids:nn {atom}
              {
                \__tenkz_model_get:nnN {##1} {cluster-of} \l_tmpb_tl
                \quark_if_no_value:NF \l_tmpb_tl
                  {
                    \str_if_eq:eeT
                      { \tl_use:N \l_tmpb_tl } { \tl_use:N \l_tmpa_tl }
                      { \seq_put_right:Nn #3 {##1} }
                  }
              }
          }
      }
      { \__tenkz_kernel_selector_reject:n {#2} }
  }
\cs_generate_variant:Nn \__tenkz_kernel_select_record:nnN { VnN }

\cs_new_protected:Npn \__tenkz_kernel_selector_reject:n #1
  {
    \bool_if:NT \l__tenkz_kernel_sel_valid_bool
      {
        \bool_set_false:N \l__tenkz_kernel_sel_valid_bool
        \msg_error:nnn {tenkz}{kernel-selector-kind} {#1}
      }
  }

% A range of cells corners a block; every occupied cell inside joins.  Other
% address kinds belong to later selector cases.
\cs_new_protected:Npn \__tenkz_kernel_select_range:nN #1#2
  {
    \tl_set:Ne \l_tmpa_tl { \__tenkz_kernel_node_item:nn {#1} {first} }
    \tl_set:Ne \l_tmpb_tl { \__tenkz_kernel_node_item:nn {#1} {second} }
    \str_if_eq:eeTF
      { \__tenkz_kernel_node_item:Nn \l_tmpa_tl {kind} }
      {cell}
      {
        \str_if_eq:eeTF
          { \__tenkz_kernel_node_item:Nn \l_tmpb_tl {kind} }
          {cell}
          {
            \exp_args:NV \__tenkz_kernel_select_corner:nNN
              \l_tmpa_tl
              \l__tenkz_kernel_sel_row_a_tl \l__tenkz_kernel_sel_col_a_tl
            \exp_args:NV \__tenkz_kernel_select_corner:nNN
              \l_tmpb_tl
              \l__tenkz_kernel_sel_row_b_tl \l__tenkz_kernel_sel_col_b_tl
            \int_step_inline:nnn
              {
                \int_min:nn
                  { \l__tenkz_kernel_sel_row_a_tl }
                  { \l__tenkz_kernel_sel_row_b_tl }
              }
              {
                \int_max:nn
                  { \l__tenkz_kernel_sel_row_a_tl }
                  { \l__tenkz_kernel_sel_row_b_tl }
              }
              {
                \int_step_inline:nnn
                  {
                    \int_min:nn
                      { \l__tenkz_kernel_sel_col_a_tl }
                      { \l__tenkz_kernel_sel_col_b_tl }
                  }
                  {
                    \int_max:nn
                      { \l__tenkz_kernel_sel_col_a_tl }
                      { \l__tenkz_kernel_sel_col_b_tl }
                  }
                  {
                    \__tenkz_kernel_select_cell_records_maybe:nnN
                      {##1} {####1} #2
                  }
              }
          }
          {
            \exp_args:Ne \__tenkz_kernel_selector_reject:n
              { \__tenkz_kernel_node_item:nn {#1} {text} }
          }
      }
      {
        \exp_args:Ne \__tenkz_kernel_selector_reject:n
          { \__tenkz_kernel_node_item:nn {#1} {text} }
      }
  }
% The atom standing on a cell, whichever way it got there.  The occupancy
% table records the cells an author claimed, and a spanning glyph claims
% several; a frame-populated site claims none, and is no less a member of a
% selection for that, so the records answer where the table is silent.
\cs_new_protected:Npn \__tenkz_kernel_select_cell:nnN #1#2#3
  {
    \prop_get:NeN \l__tenkz_kernel_cell_prop {#1-#2} #3
    \quark_if_no_value:NT #3
      {
        \__tenkz_model_map_ids:nn {atom}
          {
            \__tenkz_kernel_atom_rc:nNNN {##1}
              \l__tenkz_kernel_sel_row_tl \l__tenkz_kernel_sel_col_tl
              \l__tenkz_kernel_sel_at_bool
            \bool_if:NT \l__tenkz_kernel_sel_at_bool
              {
                \bool_lazy_and:nnT
                  { \int_compare_p:nNn { \l__tenkz_kernel_sel_row_tl } = {#1} }
                  { \int_compare_p:nNn { \l__tenkz_kernel_sel_col_tl } = {#2} }
                  { \tl_set:Nn #3 {##1} }
              }
          }
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_select_cell_records_maybe:nnN #1#2#3
  {
    \int_compare:nNnTF { \l__tenkz_kernel_basis_count_int } > {0}
      {
        \int_step_inline:nn { \l__tenkz_kernel_basis_count_int }
          {
            \__tenkz_kernel_basis_record_maybe:nnnN
              {#1}{#2}{##1} \l_tmpb_tl
            \quark_if_no_value:NF \l_tmpb_tl
              {
                \seq_if_in:NVF #3 \l_tmpb_tl
                  {
                    \__tenkz_kernel_select_record:VnN
                      \l_tmpb_tl {(#1,#2)} #3
                  }
              }
          }
      }
      {
        \__tenkz_kernel_select_cell:nnN {#1}{#2} \l_tmpb_tl
        \quark_if_no_value:NF \l_tmpb_tl
          {
            \__tenkz_kernel_select_record:VnN
              \l_tmpb_tl {(#1,#2)} #3
          }
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_select_cell_records:nnN #1#2#3
  {
    \int_set:Nn \l_tmpa_int { \seq_count:N #3 }
    \__tenkz_kernel_select_cell_records_maybe:nnN {#1}{#2} #3
    \int_compare:nNnT { \seq_count:N #3 } = { \l_tmpa_int }
      { \__tenkz_kernel_selector_reject:n {(#1,#2)} }
  }
\tl_new:N \l__tenkz_kernel_sel_row_tl
\tl_new:N \l__tenkz_kernel_sel_col_tl
\bool_new:N \l__tenkz_kernel_sel_at_bool
\cs_new_protected:Npn \__tenkz_kernel_select_corner:nNN #1#2#3
  {
    \tl_set:Ne #2 { \__tenkz_kernel_node_item:nn {#1} {row} }
    \tl_set:Ne #3 { \__tenkz_kernel_node_item:nn {#1} {col} }
  }

% THE OFFSET HULL.  Every member contributes its centre, resolved through the
% frame, and its skin's silhouette; the hull's support in a direction is the
% largest of theirs, which is the geometry stage's own fold.  Asking the frame
% rather than reading the row and column as page positions is the whole point:
% a contour drawn from integers is right only while the frame is the identity.
\cs_new_protected:Npn \__tenkz_kernel_hull_measure_reset:
  {
    \prop_clear:N \l__tenkz_kernel_hull_live_prop
    \prop_clear:N \l__tenkz_kernel_hull_measured_prop
    \prop_clear:N \l__tenkz_kernel_glyph_face_probe_prop
    % Box registers are a reusable per-picture pool.  Atom ids are global,
    % so keying allocations by id would consume one finite TeX box register
    % for every measured glyph across the whole document.
    \int_zero:N \l__tenkz_kernel_hull_box_int
  }

% The first pass measures frame-settled atoms before routes and legs ask for
% their hull.  A second pass, after wire-carried tensors settle, measures only
% the new atoms.  No author label token can therefore enter two boxes.
\cs_new_protected:Npn \__tenkz_kernel_hull_measure_settled:
  {
    \__tenkz_model_map_ids:nn {atom}
      {
        \prop_if_in:NnF \l__tenkz_kernel_hull_measured_prop {##1}
          {
            \prop_get:NnN \l__tenkz_kernel_dep_state_prop {##1}
              \l__tenkz_kernel_hull_a_tl
            \str_if_eq:VnT \l__tenkz_kernel_hull_a_tl {settled}
              { \__tenkz_kernel_hull_measure_atom:n {##1} }
          }
      }
  }

% Resolve the effective base skin shared by scheduled and on-demand probes.
\cs_new_protected:Npn \__tenkz_kernel_hull_resolve_skin:n #1
  { \__tenkz_kernel_atom_skin_base:nN {#1} \l__tenkz_kernel_hull_skin_tl }

% Materialize a validated glyph probe at most once.
\cs_new_protected:Npn \__tenkz_kernel_hull_materialize:n #1
  {
    \prop_if_in:NnF \l__tenkz_kernel_hull_live_prop {#1/xmin}
      {
        \prop_put:Nnn \l__tenkz_kernel_hull_measured_prop {#1} { }
        \__tenkz_kernel_hull_measure_glyph:n {#1}
      }
  }

% A face-only probe needs the measurement node, but must not publish temporary
% bounds as live hull support.  In particular, an ordinary labelled dot uses
% the conservative dot-label band until a hull consumer deliberately measures
% it; merely asking for its ink face must not replace that contract with the
% dot-only measurement.
\cs_new_protected:Npn \__tenkz_kernel_glyph_face_probe_materialize:n #1
  {
    \prop_if_in:NnF \l__tenkz_kernel_hull_live_prop {#1/xmin}
      {
        \prop_if_in:NnF \l__tenkz_kernel_glyph_face_probe_prop {#1}
          {
            \__tenkz_kernel_hull_measure_glyph:n {#1}
            \clist_map_inline:nn {box,family,radius,xmin,xmax,ymin,ymax}
              {
                \prop_remove:Nn \l__tenkz_kernel_hull_live_prop
                  {#1/##1}
              }
            \prop_put:Nnn \l__tenkz_kernel_glyph_face_probe_prop {#1} { }
          }
      }
  }

% Validate and materialize a record's live support.  The bool output is true
% for every non-cluster record; a cluster emits its coded diagnostic and
% leaves the live cache untouched.  A `none` record may still need synthetic
% support for an authored port even when it has no glyph ink of its own.
\cs_new_protected:Npn \__tenkz_kernel_hull_ensure_measured:nN #1#2
  {
    \bool_set_false:N #2
    \__tenkz_model_get:nnN {#1} {cluster} \l__tenkz_kernel_hull_a_tl
    \quark_if_no_value:NTF \l__tenkz_kernel_hull_a_tl
      {
        \__tenkz_kernel_hull_resolve_skin:n {#1}
        \__tenkz_kernel_hull_materialize:n {#1}
        \bool_set_true:N #2
      }
      { \msg_error:nnn {tenkz}{kernel-glyph-face-cluster}{#1} }
  }

\cs_new_protected:Npn \__tenkz_kernel_glyph_face_ensure_measured:n #1
  {
    \__tenkz_kernel_hull_resolve_skin:n {#1}
    \str_case:VnF \l__tenkz_kernel_hull_skin_tl
      {
        {dot}
          {
            \prop_if_in:NnTF
              \l__tenkz_kernel_skin_pairing_host_prop {#1}
              { \__tenkz_kernel_hull_materialize:n {#1} }
              { \__tenkz_kernel_glyph_face_probe_materialize:n {#1} }
          }
        {dots}
          {
            \prop_if_in:NnTF
              \l__tenkz_kernel_skin_pairing_host_prop {#1}
              { \__tenkz_kernel_hull_materialize:n {#1} }
              { \__tenkz_kernel_glyph_face_probe_materialize:n {#1} }
          }
      }
      { \__tenkz_kernel_hull_materialize:n {#1} }
  }

\cs_new_protected:Npn \__tenkz_kernel_hull_measure_atom:n #1
  {
    \prop_put:Nnn \l__tenkz_kernel_hull_measured_prop {#1} { }
    % A cluster carrier is a group and has no glyph of its own.
    \__tenkz_model_get:nnN {#1} {cluster} \l__tenkz_kernel_hull_a_tl
    \quark_if_no_value:NT \l__tenkz_kernel_hull_a_tl
      {
        \__tenkz_kernel_hull_resolve_skin:n {#1}
        \str_case:VnF \l__tenkz_kernel_hull_skin_tl
          {
            {dot}
              {
                \prop_if_in:NnT
                  \l__tenkz_kernel_skin_pairing_host_prop {#1}
                  { \__tenkz_kernel_hull_measure_glyph:n {#1} }
              }
            {dots}
              {
                \prop_if_in:NnT
                  \l__tenkz_kernel_skin_pairing_host_prop {#1}
                  { \__tenkz_kernel_hull_measure_glyph:n {#1} }
              }
            {none}
              {
                \prop_if_in:NnT
                  \l__tenkz_kernel_skin_pairing_host_prop {#1}
                  { \__tenkz_kernel_hull_measure_glyph:n {#1} }
              }
          }
          { \__tenkz_kernel_hull_measure_glyph:n {#1} }
      }
  }

% ---------- the equation's shared measure ---------------------------------------
% The measure is held per size class, because the class is what an author
% declares about how big a glyph is; two glyphs of one class in one equation
% are two spellings of one extent, and the equation draws them so.  An atom
% that states no class is in the `m' class, which is what the registry
% declares its default to be, so stating it and omitting it are one key and
% not two -- otherwise an equation of a bare X against an m-classed inverse
% would keep the two measures apart and draw exactly the pair of sizes this
% is here to stop.
% An ellipsis is decoration, not a name.  Its glyph carries \cdots so that
% the mark has something to measure, but it belongs to no size class: it must
% neither lend its extent to the names that do nor be stretched to theirs,
% because its layout geometry is the zero-radius point its ports are placed
% from and the ink would leave the anchor behind.
\bool_new:N \l__tenkz_kernel_eq_extent_named_bool
\tl_new:N \l__tenkz_kernel_eq_extent_skin_tl
\cs_new_protected:Npn \__tenkz_kernel_eq_extent_named:n #1
  {
    \bool_set_true:N \l__tenkz_kernel_eq_extent_named_bool
    % A declared skin is classified by the primitive it resolves to, as hull
    % measurement and ink already are, so an alias of the ellipsis cannot
    % acquire a name's measure by wearing a different word.
    \__tenkz_model_get:nnN {#1} {skin} \l__tenkz_kernel_eq_extent_skin_tl
    \quark_if_no_value:NF \l__tenkz_kernel_eq_extent_skin_tl
      {
        \exp_args:NV \__tenkz_kernel_skin_base:nN
          \l__tenkz_kernel_eq_extent_skin_tl \l__tenkz_kernel_eq_extent_skin_tl
        \str_case:VnT \l__tenkz_kernel_eq_extent_skin_tl
          { {dots}{} {dot}{} {none}{} }
          { \bool_set_false:N \l__tenkz_kernel_eq_extent_named_bool }
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_eq_extent_key:nN #1#2
  {
    \__tenkz_model_get:nnN {#1} {size} #2
    \quark_if_no_value:NT #2 { \tl_set:Nn #2 {m} }
  }
% Read one inscribed name during the measuring run and keep the largest of
% each dimension seen so far.  Width, height, and depth are kept apart: the
% height and depth answer the ascender and the descender separately, so a
% subscript in one panel never lifts the names in the others off their axis.
\cs_new_protected:Npn \__tenkz_kernel_eq_extent_record:nN #1#2
  {
    \__tenkz_kernel_eq_extent_key:nN {#1} \l__tenkz_kernel_eq_extent_key_tl
    \dim_set:Nn \l__tenkz_kernel_eq_extent_wd_dim { \box_wd:N #2 }
    \dim_set:Nn \l__tenkz_kernel_eq_extent_ht_dim { \box_ht:N #2 }
    \dim_set:Nn \l__tenkz_kernel_eq_extent_dp_dim { \box_dp:N #2 }
    \prop_get:NVNT \g__tenkz_kernel_eq_extent_prop
      \l__tenkz_kernel_eq_extent_key_tl \l__tenkz_kernel_eq_extent_tl
      {
        \dim_set:Nn \l__tenkz_kernel_eq_extent_wd_dim
          {
            \dim_max:nn { \l__tenkz_kernel_eq_extent_wd_dim }
              { \clist_item:Nn \l__tenkz_kernel_eq_extent_tl {1} }
          }
        \dim_set:Nn \l__tenkz_kernel_eq_extent_ht_dim
          {
            \dim_max:nn { \l__tenkz_kernel_eq_extent_ht_dim }
              { \clist_item:Nn \l__tenkz_kernel_eq_extent_tl {2} }
          }
        \dim_set:Nn \l__tenkz_kernel_eq_extent_dp_dim
          {
            \dim_max:nn { \l__tenkz_kernel_eq_extent_dp_dim }
              { \clist_item:Nn \l__tenkz_kernel_eq_extent_tl {3} }
          }
      }
    \prop_gput:NVe \g__tenkz_kernel_eq_extent_prop
      \l__tenkz_kernel_eq_extent_key_tl
      {
        \dim_use:N \l__tenkz_kernel_eq_extent_wd_dim ,
        \dim_use:N \l__tenkz_kernel_eq_extent_ht_dim ,
        \dim_use:N \l__tenkz_kernel_eq_extent_dp_dim
      }
  }
% Set one inscribed name on the equation's measure for its class.  Outside an
% equation, and for the measuring run itself, the name is set as it comes.
% The name is centred in the shared width and given the shared strut, so it
% keeps its own baseline and the glyph around it keeps the class geometry.
\cs_new_protected:Npn \__tenkz_kernel_eq_extent_set:nn #1#2
  {
    \__tenkz_kernel_eq_extent_named:n {#1}
    \bool_lazy_all:nTF
      {
        { \bool_if_p:N \l__tenkz_kernel_ineq_bool }
        { ! \bool_if_p:N \l__tenkz_kernel_eq_probe_bool }
        { \bool_if_p:N \l__tenkz_kernel_eq_extent_named_bool }
      }
      {
        \__tenkz_kernel_eq_extent_key:nN {#1} \l__tenkz_kernel_eq_extent_key_tl
        \prop_get:NVNTF \g__tenkz_kernel_eq_extent_prop
          \l__tenkz_kernel_eq_extent_key_tl \l__tenkz_kernel_eq_extent_tl
          {
            % The measure is a floor and never a cap: a name the measuring
            % run did not see -- one a macro brings to a panel the body does
            % not spell out -- keeps its own width rather than being pushed
            % out of the ink holding it.
            \hbox_set:Nn \l__tenkz_kernel_eq_name_box {#2}
            \hbox_to_wd:nn
              {
                \dim_max:nn
                  { \clist_item:Nn \l__tenkz_kernel_eq_extent_tl {1} }
                  { \box_wd:N \l__tenkz_kernel_eq_name_box }
              }
              {
                \tex_hss:D
                \tex_vrule:D width \c_zero_dim
                  height \clist_item:Nn \l__tenkz_kernel_eq_extent_tl {2}
                  depth \clist_item:Nn \l__tenkz_kernel_eq_extent_tl {3}
                \scan_stop:
                \box_use:N \l__tenkz_kernel_eq_name_box
                \tex_hss:D
              }
          }
          {#2}
      }
      {#2}
  }

% A standalone glyph belongs to its class and slots, not to the width of its
% name.  Measure the natural labelled node first, then the same style without
% content.  If the name enlarged either axis, keep the natural box for label
% ink but publish the empty node as the glyph silhouette; the annotation
% stage will place that box at the atom's ordinary label station.  Equations
% retain their shared name measure and never enter this probe.
\cs_new_protected:Npn \__tenkz_kernel_hull_standalone_spill:n #1
  {
    \__tenkz_kernel_eq_extent_named:n {#1}
    \bool_lazy_all:nT
      {
        { ! \bool_if_p:N \l__tenkz_kernel_ineq_bool }
        { ! \tl_if_empty_p:N \l__tenkz_kernel_hull_a_tl }
        { \bool_if_p:N \l__tenkz_kernel_eq_extent_named_bool }
      }
      {
        \tl_set_eq:NN \l__tenkz_kernel_hull_natural_name_tl
          \l__tenkz_kernel_hull_measure_name_tl
        \__tenkz_kernel_hull_anchor_x:nN {west}
          \l__tenkz_kernel_hull_west_dim
        \__tenkz_kernel_hull_anchor_x:nN {east}
          \l__tenkz_kernel_hull_east_dim
        \__tenkz_kernel_hull_anchor_y:nN {south}
          \l__tenkz_kernel_hull_south_dim
        \__tenkz_kernel_hull_anchor_y:nN {north}
          \l__tenkz_kernel_hull_north_dim
        \dim_set:Nn \l__tenkz_kernel_hull_natural_wd_dim
          { \l__tenkz_kernel_hull_east_dim - \l__tenkz_kernel_hull_west_dim }
        \dim_set:Nn \l__tenkz_kernel_hull_natural_ht_dim
          { \l__tenkz_kernel_hull_north_dim - \l__tenkz_kernel_hull_south_dim }
        \tl_put_right:Nn \l__tenkz_kernel_hull_measure_name_tl {_floor}
        \group_begin:
          \use:c {tenkz@bboxcapturefalse}
          \bool_set_true:N \l__tenkz_kernel_extent_floor_probe_bool
          % Geometry styles are materialized token lists, so rebuild the
          % floor style while the probe flag is live.  In particular, a ring
          % must not carry its natural name's corner-safe minimum width into
          % the supposedly empty measurement.
          \exp_args:NnV \__tenkz_kernel_r_atom_glyph_style:nnN
            {#1} \l__tenkz_kernel_hull_skin_tl
            \l__tenkz_kernel_hull_b_tl
          \__tenkz_kernel_r_affine_page_padding:n {#1}
          \__tenkz_kernel_r_affine_page_style:N
            \l__tenkz_kernel_hull_b_tl
          \exp_args:NVV \__tenkz_render_measure_node:nnn
            \l__tenkz_kernel_hull_b_tl
            \l__tenkz_kernel_hull_measure_name_tl
            { }
        \group_end:
        \__tenkz_kernel_hull_anchor_x:nN {west}
          \l__tenkz_kernel_hull_west_dim
        \__tenkz_kernel_hull_anchor_x:nN {east}
          \l__tenkz_kernel_hull_east_dim
        \__tenkz_kernel_hull_anchor_y:nN {south}
          \l__tenkz_kernel_hull_south_dim
        \__tenkz_kernel_hull_anchor_y:nN {north}
          \l__tenkz_kernel_hull_north_dim
        \dim_set:Nn \l__tenkz_kernel_hull_floor_wd_dim
          { \l__tenkz_kernel_hull_east_dim - \l__tenkz_kernel_hull_west_dim }
        \dim_set:Nn \l__tenkz_kernel_hull_floor_ht_dim
          { \l__tenkz_kernel_hull_north_dim - \l__tenkz_kernel_hull_south_dim }
        \bool_lazy_or:nnTF
          {
            \dim_compare_p:nNn
              { \l__tenkz_kernel_hull_natural_wd_dim } >
              { \l__tenkz_kernel_hull_floor_wd_dim }
          }
          {
            \dim_compare_p:nNn
              { \l__tenkz_kernel_hull_natural_ht_dim } >
              { \l__tenkz_kernel_hull_floor_ht_dim }
          }
          { \prop_put:Nnn \l__tenkz_kernel_hull_live_prop {#1/spill} { } }
          {
            \tl_set_eq:NN \l__tenkz_kernel_hull_measure_name_tl
              \l__tenkz_kernel_hull_natural_name_tl
          }
      }
  }

% Author label tokens enter one unturned box.  Each glyph-local consumer
% applies the inverse carrier turn around a copy; a deferred page-space label
% copies the raw box directly.  Sizing therefore cannot execute the author's
% tokens twice, and split labels stay page-upright.
\cs_new_protected:Npn \__tenkz_kernel_hull_measure_glyph:n #1
  {
    \int_incr:N \l__tenkz_kernel_hull_box_int
    \tl_set:Ne \l__tenkz_kernel_hull_box_tl
      {
        l__tenkz_kernel_hull_box_
        \int_use:N \l__tenkz_kernel_hull_box_int _box
      }
    \cs_if_exist:cF { \tl_use:N \l__tenkz_kernel_hull_box_tl }
      { \box_new:c { \tl_use:N \l__tenkz_kernel_hull_box_tl } }
    \prop_put:NnV \l__tenkz_kernel_hull_live_prop {#1/box}
      \l__tenkz_kernel_hull_box_tl
    \__tenkz_model_get:nnN {#1} {label} \l__tenkz_kernel_hull_a_tl
    \quark_if_no_value:NT \l__tenkz_kernel_hull_a_tl
      { \tl_clear:N \l__tenkz_kernel_hull_a_tl }
    \str_case:Vn \l__tenkz_kernel_hull_skin_tl
      {
        {dot}  { \tl_clear:N \l__tenkz_kernel_hull_a_tl }
        {dots} { \tl_set:Nn \l__tenkz_kernel_hull_a_tl { \cdots } }
        {none} { \tl_clear:N \l__tenkz_kernel_hull_a_tl }
      }
    \str_if_eq:VnTF \l__tenkz_kernel_hull_skin_tl {dot}
      {
        \prop_if_in:NnTF
          \l__tenkz_kernel_skin_pairing_host_prop {#1}
          {
            \exp_args:NnV \__tenkz_kernel_r_atom_glyph_style:nnN
              {#1} \l__tenkz_kernel_hull_skin_tl
              \l__tenkz_kernel_hull_b_tl
          }
          {
            \__tenkz_kernel_r_atom_dot_measure_style:nN {#1}
              \l__tenkz_kernel_hull_b_tl
          }
      }
      {
        \str_if_eq:VnTF \l__tenkz_kernel_hull_skin_tl {dots}
          {
            \prop_if_in:NnTF
              \l__tenkz_kernel_skin_pairing_host_prop {#1}
              {
                \exp_args:NnV \__tenkz_kernel_r_atom_glyph_style:nnN
                  {#1} \l__tenkz_kernel_hull_skin_tl
                  \l__tenkz_kernel_hull_b_tl
              }
              {
                \tl_set:Nn \l__tenkz_kernel_hull_b_tl
                  {tenkz~kernel~dots~geometry}
              }
          }
          {
            \exp_args:NnV \__tenkz_kernel_r_atom_glyph_style:nnN
              {#1} \l__tenkz_kernel_hull_skin_tl
              \l__tenkz_kernel_hull_b_tl
          }
      }
    \__tenkz_kernel_r_carrier_basis:n {#1}
    \__tenkz_kernel_r_affine_page_padding:n {#1}
    \__tenkz_kernel_r_affine_page_style:N \l__tenkz_kernel_hull_b_tl
    \bool_if:NF \l__tenkz_kernel_r_basis_plane_bool
      { \__tenkz_kernel_r_turn:nN {#1} \l__tenkz_kernel_r_ink_turn_tl }
    \tl_set:Ne \l__tenkz_kernel_hull_measure_name_tl
      { tenkz_hull_\int_use:N \g__tenkz_kernel_picture_int _#1 }
    \group_begin:
      \use:c {tenkz@bboxcapturefalse}
      \exp_args:NVV \__tenkz_render_measure_node:nnn
        \l__tenkz_kernel_hull_b_tl
        \l__tenkz_kernel_hull_measure_name_tl
        {
          \hbox_gset:cn { \tl_use:N \l__tenkz_kernel_hull_box_tl }
            {
              % Ellipsis glyphs use the live renderer's normal math size.
              % Inscribed labels use the audited smaller label size.
              \__tenkz_kernel_eq_extent_set:nn {#1}
                {
                  $
                    \str_if_eq:VnF \l__tenkz_kernel_hull_skin_tl {dots}
                      { \use:c {tenkz@labelsize} }
                    \tl_use:N \l__tenkz_kernel_hull_a_tl $
                }
            }
          \__tenkz_kernel_r_upright:n
            {
              \exp_args:Nc \tex_copy:D
                { \tl_use:N \l__tenkz_kernel_hull_box_tl }
            }
        }
    \group_end:
    \__tenkz_kernel_hull_standalone_spill:n {#1}
    % The measuring run reads the name it has just set.  An unnamed glyph --
    % a bead, a junction, a void site -- shares nothing, so it contributes
    % nothing to the class measure.
    \__tenkz_kernel_eq_extent_named:n {#1}
    \bool_lazy_all:nT
      {
        { \bool_if_p:N \l__tenkz_kernel_eq_probe_bool }
        { ! \tl_if_empty_p:N \l__tenkz_kernel_hull_a_tl }
        { \bool_if_p:N \l__tenkz_kernel_eq_extent_named_bool }
      }
      {
        \exp_args:Nnc \__tenkz_kernel_eq_extent_record:nN {#1}
          { \tl_use:N \l__tenkz_kernel_hull_box_tl }
        \__tenkz_kernel_eq_ringpad_record:n {#1}
      }
    \tl_set:Ne \l__tenkz_kernel_hull_shape_tl
      {
        \use:c
          { pgf@sh@ns@\tl_use:N \l__tenkz_kernel_hull_measure_name_tl }
      }
    \str_if_eq:VnTF \l__tenkz_kernel_hull_shape_tl {isosceles~triangle}
      {
        % Both canonical isometries measure as one pgf triangle; the resolved
        % base says which end of its own east axis the apex stands on, and
        % the fold family carries that answer to every contour and route.
        \str_if_eq:VnTF \l__tenkz_kernel_hull_skin_tl {triwest}
          {
            \prop_put:Nnn \l__tenkz_kernel_hull_live_prop {#1/family}
              {triwest}
          }
          {
            \prop_put:Nnn \l__tenkz_kernel_hull_live_prop {#1/family}
              {triangle}
          }
        \__tenkz_kernel_hull_anchor_x:nN {apex}
          \l__tenkz_kernel_hull_xone_dim
        \__tenkz_kernel_hull_anchor_y:nN {apex}
          \l__tenkz_kernel_hull_yone_dim
        \__tenkz_kernel_hull_anchor_x:nN {left~corner}
          \l__tenkz_kernel_hull_xtwo_dim
        \__tenkz_kernel_hull_anchor_y:nN {left~corner}
          \l__tenkz_kernel_hull_ytwo_dim
        \__tenkz_kernel_hull_anchor_x:nN {right~corner}
          \l__tenkz_kernel_hull_xthree_dim
        \__tenkz_kernel_hull_anchor_y:nN {right~corner}
          \l__tenkz_kernel_hull_ythree_dim
        \dim_set:Nn \l__tenkz_kernel_hull_west_dim
          {
            \dim_min:nn { \l__tenkz_kernel_hull_xone_dim }
              {
                \dim_min:nn { \l__tenkz_kernel_hull_xtwo_dim }
                  { \l__tenkz_kernel_hull_xthree_dim }
              }
          }
        \dim_set:Nn \l__tenkz_kernel_hull_east_dim
          {
            \dim_max:nn { \l__tenkz_kernel_hull_xone_dim }
              {
                \dim_max:nn { \l__tenkz_kernel_hull_xtwo_dim }
                  { \l__tenkz_kernel_hull_xthree_dim }
              }
          }
        \dim_set:Nn \l__tenkz_kernel_hull_south_dim
          {
            \dim_min:nn { \l__tenkz_kernel_hull_yone_dim }
              {
                \dim_min:nn { \l__tenkz_kernel_hull_ytwo_dim }
                  { \l__tenkz_kernel_hull_ythree_dim }
              }
          }
        \dim_set:Nn \l__tenkz_kernel_hull_north_dim
          {
            \dim_max:nn { \l__tenkz_kernel_hull_yone_dim }
              {
                \dim_max:nn { \l__tenkz_kernel_hull_ytwo_dim }
                  { \l__tenkz_kernel_hull_ythree_dim }
              }
          }
      }
      {
        % The measured pill publishes the roundrect family with the live
        % named pill-cap radius, in the anchors' outer envelope: the path
        % radius plus the half-stroke the border anchors already include.
        % No box-shaped record may stand in for pill ink.
        \str_if_eq:VnTF \l__tenkz_kernel_hull_skin_tl {roundrect}
          {
            \prop_put:Nnn \l__tenkz_kernel_hull_live_prop {#1/family}
              {roundrect}
            \prop_put:Nne \l__tenkz_kernel_hull_live_prop {#1/radius}
              {
                \fp_eval:n
                  {
                    ( \dim_to_fp:n { \__tenkz_dim:n {pillcap} }
                      + \dim_to_fp:n { \__tenkz_dim:n {wirewidth} } / 2 )
                    / \dim_to_fp:n { \use:c {tenkz@pitch} }
                  }
              }
          }
          {
            \prop_put:Nnn \l__tenkz_kernel_hull_live_prop {#1/family}
              {rect}
          }
        \__tenkz_kernel_hull_anchor_x:nN {west}
          \l__tenkz_kernel_hull_west_dim
        \__tenkz_kernel_hull_anchor_x:nN {east}
          \l__tenkz_kernel_hull_east_dim
        \__tenkz_kernel_hull_anchor_y:nN {south}
          \l__tenkz_kernel_hull_south_dim
        \__tenkz_kernel_hull_anchor_y:nN {north}
          \l__tenkz_kernel_hull_north_dim
      }
    \__tenkz_kernel_hull_publish:nnN {#1} {xmin}
      \l__tenkz_kernel_hull_west_dim
    \__tenkz_kernel_hull_publish:nnN {#1} {xmax}
      \l__tenkz_kernel_hull_east_dim
    \__tenkz_kernel_hull_publish:nnN {#1} {ymin}
      \l__tenkz_kernel_hull_south_dim
    \__tenkz_kernel_hull_publish:nnN {#1} {ymax}
      \l__tenkz_kernel_hull_north_dim
  }

% Page x and y of one anchor on the measurement node the probe above leaves
% standing.  Every silhouette family reads its bounds through this pair, so a
% family added later cannot address that node by a spelling of its own.
\cs_new_protected:Npn \__tenkz_kernel_hull_anchor_x:nN #1#2
  {
    \pgfextractx #2
      {
        \pgfpointanchor
          {\tl_use:N \l__tenkz_kernel_hull_measure_name_tl}{#1}
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_hull_anchor_y:nN #1#2
  {
    \pgfextracty #2
      {
        \pgfpointanchor
          {\tl_use:N \l__tenkz_kernel_hull_measure_name_tl}{#1}
      }
  }

% Live hull bounds are pitch factors: geometry folds supports in pitch units
% and only the stroke stage multiplies back.
\cs_new_protected:Npn \__tenkz_kernel_hull_publish:nnN #1#2#3
  {
    \prop_put:Nee \l__tenkz_kernel_hull_live_prop {#1/#2}
      {
        \fp_eval:n
          {
            \dim_to_fp:n { #3 }
            / \dim_to_fp:n { \use:c {tenkz@pitch} }
          }
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_hull_obstacles:NN #1#2
  {
    \seq_clear:N #2
    \seq_map_inline:Nn #1
      {
        \__tenkz_kernel_r_xy:nNN {##1}
          \l__tenkz_kernel_hull_x_fp \l__tenkz_kernel_hull_y_fp
        \prop_get:NnNTF \l__tenkz_kernel_hull_live_prop {##1/xmin}
          \l__tenkz_kernel_hull_xmin_tl
          {
            \prop_get:NnN \l__tenkz_kernel_hull_live_prop {##1/xmax}
              \l__tenkz_kernel_hull_xmax_tl
            \prop_get:NnN \l__tenkz_kernel_hull_live_prop {##1/ymin}
              \l__tenkz_kernel_hull_ymin_tl
            \prop_get:NnN \l__tenkz_kernel_hull_live_prop {##1/ymax}
              \l__tenkz_kernel_hull_ymax_tl
            \prop_get:NnN \l__tenkz_kernel_hull_live_prop {##1/family}
              \l__tenkz_kernel_hull_shape_tl
            \quark_if_no_value:NT \l__tenkz_kernel_hull_shape_tl
              { \tl_set:Nn \l__tenkz_kernel_hull_shape_tl {rect} }
            % Only the roundrect family publishes a corner radius; every
            % other measured family folds with a sharp record.
            \prop_get:NnN \l__tenkz_kernel_hull_live_prop {##1/radius}
              \l__tenkz_kernel_hull_radius_tl
            \quark_if_no_value:NT \l__tenkz_kernel_hull_radius_tl
              { \tl_set:Nn \l__tenkz_kernel_hull_radius_tl {0} }
            \fp_set:Nn \l__tenkz_kernel_hull_xoff_fp
              {
                ( \l__tenkz_kernel_hull_xmin_tl
                  + \l__tenkz_kernel_hull_xmax_tl ) / 2
              }
            \fp_set:Nn \l__tenkz_kernel_hull_yoff_fp
              {
                ( \l__tenkz_kernel_hull_ymin_tl
                  + \l__tenkz_kernel_hull_ymax_tl ) / 2
              }
            \__tenkz_kernel_r_carrier_basis:n {##1}
            \bool_if:NTF \l__tenkz_kernel_r_basis_plane_bool
              {
                \__tenkz_geom_basis_apply:nnnnnnNN
                  { \l__tenkz_kernel_r_basis_ex_tl }
                  { \l__tenkz_kernel_r_basis_ey_tl }
                  { \l__tenkz_kernel_r_basis_nx_tl }
                  { \l__tenkz_kernel_r_basis_ny_tl }
                  { \l__tenkz_kernel_hull_xoff_fp }
                  { \l__tenkz_kernel_hull_yoff_fp }
                  \l__tenkz_kernel_r_x_tl \l__tenkz_kernel_r_y_tl
                \seq_put_right:Ne #2
                  {
                    {affine}
                    { \tl_use:N \l__tenkz_kernel_hull_shape_tl }
                    { \fp_eval:n { ( \l__tenkz_kernel_hull_xmax_tl
                        - \l__tenkz_kernel_hull_xmin_tl ) / 2 } }
                    { \fp_eval:n { ( \l__tenkz_kernel_hull_ymax_tl
                        - \l__tenkz_kernel_hull_ymin_tl ) / 2 } }
                    { \tl_use:N \l__tenkz_kernel_hull_radius_tl }
                    { \l__tenkz_kernel_r_basis_ex_tl }
                    { \l__tenkz_kernel_r_basis_ey_tl }
                    { \l__tenkz_kernel_r_basis_nx_tl }
                    { \l__tenkz_kernel_r_basis_ny_tl }
                    { \fp_eval:n { \l__tenkz_kernel_hull_x_fp
                        + \l__tenkz_kernel_r_x_tl } }
                    { \fp_eval:n { \l__tenkz_kernel_hull_y_fp
                        + \l__tenkz_kernel_r_y_tl } }
                  }
              }
              {
                \__tenkz_kernel_r_turn:nN {##1}
                  \l__tenkz_kernel_hull_turn_tl
                \fp_set:Nn \l__tenkz_kernel_hull_xrot_fp
                  {
                    \l__tenkz_kernel_hull_xoff_fp
                      * cosd( \l__tenkz_kernel_hull_turn_tl )
                    - \l__tenkz_kernel_hull_yoff_fp
                      * sind( \l__tenkz_kernel_hull_turn_tl )
                  }
                \fp_set:Nn \l__tenkz_kernel_hull_yrot_fp
                  {
                    \l__tenkz_kernel_hull_xoff_fp
                      * sind( \l__tenkz_kernel_hull_turn_tl )
                    + \l__tenkz_kernel_hull_yoff_fp
                      * cosd( \l__tenkz_kernel_hull_turn_tl )
                  }
                \seq_put_right:Ne #2
                  {
                    { \tl_use:N \l__tenkz_kernel_hull_shape_tl }
                    { \fp_eval:n { ( \l__tenkz_kernel_hull_xmax_tl
                        - \l__tenkz_kernel_hull_xmin_tl ) / 2 } }
                    { \fp_eval:n { ( \l__tenkz_kernel_hull_ymax_tl
                        - \l__tenkz_kernel_hull_ymin_tl ) / 2 } }
                    { \tl_use:N \l__tenkz_kernel_hull_radius_tl }
                    { \tl_use:N \l__tenkz_kernel_hull_turn_tl }
                    { \fp_eval:n { \l__tenkz_kernel_hull_x_fp
                        + \l__tenkz_kernel_hull_xrot_fp } }
                    { \fp_eval:n { \l__tenkz_kernel_hull_y_fp
                        + \l__tenkz_kernel_hull_yrot_fp } }
                  }
              }
          }
          {
            \__tenkz_kernel_hull_skin:nN {##1}
              \l__tenkz_kernel_hull_skin_tl
            \seq_put_right:Ne #2
              {
                \exp_not:V \l__tenkz_kernel_hull_skin_tl
                { \fp_eval:n { \l__tenkz_kernel_hull_x_fp } }
                { \fp_eval:n { \l__tenkz_kernel_hull_y_fp } }
              }
            % The dot itself follows the affine chart, but its external label
            % occupies a page-space band.  Publish both supports so a
            % contracting chart cannot contract the label clearance.
            \__tenkz_kernel_atom_skin_base:nN {##1}
              \l__tenkz_kernel_hull_a_tl
            \str_if_eq:VnT \l__tenkz_kernel_hull_a_tl {dot}
              {
                \__tenkz_model_get:nnN {##1} {label}
                  \l__tenkz_kernel_hull_b_tl
                \quark_if_no_value:NF \l__tenkz_kernel_hull_b_tl
                  {
                    \__tenkz_model_get:nnN {##1} {cluster-of}
                      \l__tenkz_kernel_hull_size_tl
                    \quark_if_no_value:NT \l__tenkz_kernel_hull_size_tl
                      {
                        \__tenkz_kernel_r_carrier_basis:n {##1}
                        \bool_if:NT \l__tenkz_kernel_r_basis_plane_bool
                          {
                            \seq_put_right:Ne #2
                              {
                                {circle}
                                { \__tenkz_metric_ratio:n {dotlabelband} }
                                {0}{0}{0}
                                { \fp_eval:n { \l__tenkz_kernel_hull_x_fp } }
                                { \fp_eval:n { \l__tenkz_kernel_hull_y_fp } }
                              }
                          }
                      }
                  }
              }
          }
      }
  }
% An unmeasured canonical isometry folds under the triangle family named by
% its apex direction; both orientations share one square of half-extents and
% one turn, so the pair cannot drift apart.
\cs_new_protected:Npn \__tenkz_kernel_hull_tri:nnN #1#2#3
  {
    \__tenkz_kernel_r_turn:nN {#1} \l__tenkz_kernel_hull_turn_tl
    \tl_set:Ne #3
      {
        {#2}
        { \__tenkz_metric_ratio:n {glyphbox} }
        { \__tenkz_metric_ratio:n {glyphbox} }
        {0}
        { \tl_use:N \l__tenkz_kernel_hull_turn_tl }
      }
  }
% a record's silhouette as the support fold wants it: family and half-extents,
% corner radius, and the angle its own frame turns it by
\cs_new_protected:Npn \__tenkz_kernel_hull_skin:nN #1#2
  {
    \__tenkz_kernel_atom_skin_base:nN {#1} \l__tenkz_kernel_hull_a_tl
    \str_case:VnF \l__tenkz_kernel_hull_a_tl
      {
        {dot}
          {
            % a labelled dot is as big as the band its label stands in:
            % a contour that clears the dot but cuts the label clears nothing
            \__tenkz_model_get:nnN {#1} {label} \l_tmpa_tl
            \__tenkz_model_get:nnN {#1} {cluster-of} \l_tmpb_tl
            \__tenkz_kernel_hull_dot_radius:nN {#1}
              \l__tenkz_kernel_hull_radius_tl
            \__tenkz_kernel_r_carrier_basis:n {#1}
            \tl_set:Ne #2
              {
                {circle}
                {
                  \fp_eval:n
                    {
                      \quark_if_no_value_p:N \l_tmpb_tl
                        ? ( \quark_if_no_value_p:N \l_tmpa_tl
                              ? \l__tenkz_kernel_hull_radius_tl
                              : ( \bool_if_p:N
                                    \l__tenkz_kernel_r_basis_plane_bool
                                  ? \l__tenkz_kernel_hull_radius_tl
                                  : max(
                                      \l__tenkz_kernel_hull_radius_tl ,
                                      \__tenkz_metric_ratio:n
                                        {dotlabelband} ) ) )
                        : \__tenkz_metric_ratio:n {clusterdot} / 2
                    }
                }
                {0}{0}{0}
              }
          }
        {dots}  { \tl_set:Nn #2 { {circle}{0}{0}{0}{0} } }
        {none}  { \tl_set:Nn #2 { {circle}{0}{0}{0}{0} } }
        {roundrect}
          {
            % An unmeasured pill spans exactly as the generic glyph below
            % does, under its own family: the caps subtract the live named
            % pill-cap radius from the sharp corners.
            \__tenkz_kernel_hull_span:nnnN {#1} {roundrect}
              {
                \fp_eval:n
                  {
                    \dim_to_fp:n { \__tenkz_dim:n {pillcap} }
                    / \dim_to_fp:n { \use:c {tenkz@pitch} }
                  }
              }
              #2
          }
        {tri}
          {
            \__tenkz_kernel_hull_tri:nnN {#1} {triangle} #2
          }
        {triwest}
          {
            % The mirrored isometry is the same measured triangle read
            % apex-west; only the silhouette family changes, so the record
            % keeps the atom's own turn and every port stays in its own axes.
            \__tenkz_kernel_hull_tri:nnN {#1} {triwest} #2
          }
      }
      { \__tenkz_kernel_hull_span:nnnN {#1} {rect} {0} #2 }
    \__tenkz_kernel_r_carrier_basis:n {#1}
    \bool_if:NT \l__tenkz_kernel_r_basis_plane_bool
      {
        \exp_last_unbraced:NV
          \__tenkz_kernel_hull_skin_affine:nnnnnN #2 #2
      }
  }
% A spanned glyph folds under family #2 over the atom's own half-extents and
% its own turn, with corner radius #3 -- zero for a sharp silhouette, the live
% cap for a rounded one.  Sharp and rounded therefore cannot drift apart.
\cs_new_protected:Npn \__tenkz_kernel_hull_span:nnnN #1#2#3#4
  {
    \__tenkz_kernel_hull_half:nnN {#1} {wide}  \l__tenkz_kernel_hull_a_tl
    \__tenkz_kernel_hull_half:nnN {#1} {wires} \l__tenkz_kernel_hull_b_tl
    \__tenkz_kernel_r_turn:nN {#1} \l__tenkz_kernel_hull_turn_tl
    \tl_set:Ne #4
      {
        {#2}
        { \tl_use:N \l__tenkz_kernel_hull_a_tl }
        { \tl_use:N \l__tenkz_kernel_hull_b_tl }
        {#3}
        { \tl_use:N \l__tenkz_kernel_hull_turn_tl }
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_hull_skin_affine:nnnnnN
    #1#2#3#4#5#6
  {
    \tl_set:Ne #6
      {
        {affine}{#1}{#2}{#3}{#4}
        { \l__tenkz_kernel_r_basis_ex_tl }
        { \l__tenkz_kernel_r_basis_ey_tl }
        { \l__tenkz_kernel_r_basis_nx_tl }
        { \l__tenkz_kernel_r_basis_ny_tl }
      }
  }
% Radius of an ordinary dot's declared size class, in pitch units.  Cluster
% children keep their sub-frame radius in the caller above.
\cs_new_protected:Npn \__tenkz_kernel_hull_dot_radius:nN #1#2
  {
    \__tenkz_model_get:nnN {#1} {size} \l__tenkz_kernel_hull_size_tl
    \quark_if_no_value:NTF \l__tenkz_kernel_hull_size_tl
      { \tl_set:Ne #2 { \__tenkz_metric_ratio:n {dotdia} / 2 } }
      {
        \str_case:Vn \l__tenkz_kernel_hull_size_tl
          {
            {s}
              {
                \tl_set:Ne #2
                  { \__tenkz_metric_ratio:n {clusterdot} / 2 }
              }
            {m}
              {
                \tl_set:Ne #2
                  { \__tenkz_metric_ratio:n {dotdia} / 2 }
              }
            {l}
              {
                \tl_set:Ne #2
                  { \__tenkz_metric_ratio:n {glyphbox} / 2 }
              }
          }
      }
  }
% half-extent of a spanned glyph along one axis, in pitch units: half the
% inscribed-glyph minimum, grown by the cells the span covers beyond its own
\cs_new_protected:Npn \__tenkz_kernel_hull_half:nnN #1#2#3
  {
    \__tenkz_model_get:nnN {#1} {#2} \l_tmpa_tl
    \quark_if_no_value:NT \l_tmpa_tl { \tl_set:Nn \l_tmpa_tl {1} }
    \tl_set:Ne #3
      {
        \fp_eval:n
          {
            \__tenkz_metric_ratio:n {glyphbox} / 2
            + ( \l_tmpa_tl - 1 ) / 2
          }
      }
  }

% The scale of the frame a selection's members sit on.  A clearance is a
% clearance ON THE FRAME: four spins of a cluster stand a quarter-pitch apart,
% and a full-pitch gap between them would swallow the neighbours they did not
% name.  A mixed selection takes the coarsest scale it contains.
\cs_new_protected:Npn \__tenkz_kernel_hull_scale:NN #1#2
  {
    \tl_set:Nn #2 {0}
    \seq_map_inline:Nn #1
      {
        \__tenkz_model_get:nnN {##1} {cluster-of} \l_tmpa_tl
        \tl_set:Ne #2
          {
            \fp_eval:n
              {
                max( #2 ,
                     \quark_if_no_value_p:N \l_tmpa_tl
                       ? 1 : \__tenkz_metric_ratio:n {clusterstep} )
              }
          }
      }
  }

% The axes a selection stands in: the mean of its members' turns, taken as a
% mean direction so that a selection straddling the wrap of the circle is not
% averaged into its opposite.  A flat selection returns zero exactly, which
% is what keeps a page-axis contour a page-axis contour.
\fp_new:N \l__tenkz_kernel_hull_cos_fp
\fp_new:N \l__tenkz_kernel_hull_sin_fp
\tl_new:N \l__tenkz_kernel_hull_ex_tl
\tl_new:N \l__tenkz_kernel_hull_ey_tl
\tl_new:N \l__tenkz_kernel_hull_nx_tl
\tl_new:N \l__tenkz_kernel_hull_ny_tl
\tl_new:N \l__tenkz_kernel_hull_ux_tl
\tl_new:N \l__tenkz_kernel_hull_uy_tl
\tl_new:N \l__tenkz_kernel_hull_vx_tl
\tl_new:N \l__tenkz_kernel_hull_vy_tl
\tl_new:N \l__tenkz_kernel_hull_basis_row_tl
\tl_new:N \l__tenkz_kernel_hull_basis_col_tl
\tl_new:N \l__tenkz_kernel_hull_basis_parent_tl
\tl_new:N \l__tenkz_kernel_hull_basis_record_tl
\bool_new:N \l__tenkz_kernel_hull_chart_bool
\bool_new:N \l__tenkz_kernel_hull_basis_at_bool
\cs_new_protected:Npn \__tenkz_kernel_hull_turn:NN #1#2
  {
    \fp_zero:N \l__tenkz_kernel_hull_cos_fp
    \fp_zero:N \l__tenkz_kernel_hull_sin_fp
    \seq_map_inline:Nn #1
      {
        \__tenkz_kernel_r_turn:nN {##1} \l__tenkz_kernel_hull_turn_tl
        \fp_add:Nn \l__tenkz_kernel_hull_cos_fp
          { cosd( \l__tenkz_kernel_hull_turn_tl ) }
        \fp_add:Nn \l__tenkz_kernel_hull_sin_fp
          { sind( \l__tenkz_kernel_hull_turn_tl ) }
      }
    \tl_set:Ne #2
      {
        \fp_eval:n
          { atand( \l__tenkz_kernel_hull_sin_fp ,
                   \l__tenkz_kernel_hull_cos_fp ) }
      }
  }

% The cell a selection member stands in, and the record that answers for it:
% the member's own address, or -- for a generated cluster child, which
% carries no address of its own -- its carrier's.  #5 is false when the
% member stands in no cell either way, which is exactly when a selection has
% no common chart.  The basis and the annotation layering read a selection's
% cells through this one production, so a selection one of them can chart is
% a selection the other can place.
\tl_new:N \l__tenkz_kernel_hull_member_tl
\cs_new_protected:Npn \__tenkz_kernel_hull_member_rc:nNNN #1#2#3#4
  {
    \tl_set:Nn \l__tenkz_kernel_hull_member_tl {#1}
    \__tenkz_kernel_atom_rc:nNNN {#1} #2#3#4
    \bool_if:NF #4
      {
        \__tenkz_model_get:nnN {#1} {cluster-of}
          \l__tenkz_kernel_hull_basis_parent_tl
        \quark_if_no_value:NF \l__tenkz_kernel_hull_basis_parent_tl
          {
            \prop_get:NVN \l__tenkz_kernel_named_prop
              \l__tenkz_kernel_hull_basis_parent_tl
              \l__tenkz_kernel_hull_basis_record_tl
            \quark_if_no_value:NF \l__tenkz_kernel_hull_basis_record_tl
              {
                \exp_args:NV \__tenkz_kernel_atom_rc:nNNN
                  \l__tenkz_kernel_hull_basis_record_tl #2#3#4
                \bool_if:NT #4
                  {
                    \tl_set_eq:NN \l__tenkz_kernel_hull_member_tl
                      \l__tenkz_kernel_hull_basis_record_tl
                  }
              }
          }
      }
  }

% The complete east/north basis of a selection.  Affine pictures have one
% global linear part.  Circle selections retain the established mean tangent,
% represented here as an orthonormal pair so all downstream consumers use the
% same state shape.
\cs_new_protected:Npn \__tenkz_kernel_hull_basis:NNNNN #1#2#3#4#5
  {
    \bool_set_true:N \l__tenkz_kernel_hull_chart_bool
    \seq_map_inline:Nn #1
      {
        \__tenkz_kernel_hull_member_rc:nNNN {##1}
          \l__tenkz_kernel_hull_basis_row_tl
          \l__tenkz_kernel_hull_basis_col_tl
          \l__tenkz_kernel_hull_basis_at_bool
        \bool_if:NF \l__tenkz_kernel_hull_basis_at_bool
          { \bool_set_false:N \l__tenkz_kernel_hull_chart_bool }
      }
    \__tenkz_geom_frame_if_affine:nTF {kpic}
      {
        \bool_if:NTF \l__tenkz_kernel_hull_chart_bool
          { \__tenkz_geom_local_basis:nnnNNNN {kpic}{1}{1} #2#3#4#5 }
          { \__tenkz_kernel_hull_basis_orthogonal:NNNNN #1#2#3#4#5 }
      }
      { \__tenkz_kernel_hull_basis_orthogonal:NNNNN #1#2#3#4#5 }
  }
\cs_new_protected:Npn \__tenkz_kernel_hull_basis_orthogonal:NNNNN
    #1#2#3#4#5
  {
    \__tenkz_kernel_hull_turn:NN #1 \l__tenkz_kernel_hull_turn_tl
    \tl_set:Ne #2
      { \fp_eval:n { cosd(\l__tenkz_kernel_hull_turn_tl) } }
    \tl_set:Ne #3
      { \fp_eval:n { sind(\l__tenkz_kernel_hull_turn_tl) } }
    \tl_set:Ne #4
      { \fp_eval:n { -sind(\l__tenkz_kernel_hull_turn_tl) } }
    \tl_set:Ne #5
      { \fp_eval:n { cosd(\l__tenkz_kernel_hull_turn_tl) } }
  }

\cs_new_protected:Npn \__tenkz_kernel_hull_basis_dual:
  {
    \__tenkz_geom_basis_dual:nnnnNNNN
      { \l__tenkz_kernel_hull_ex_tl }
      { \l__tenkz_kernel_hull_ey_tl }
      { \l__tenkz_kernel_hull_nx_tl }
      { \l__tenkz_kernel_hull_ny_tl }
      \l__tenkz_kernel_hull_ux_tl
      \l__tenkz_kernel_hull_uy_tl
      \l__tenkz_kernel_hull_vx_tl
      \l__tenkz_kernel_hull_vy_tl
  }

% Maximum coordinate against one row of the dual basis.  The pad is a page
% distance, so lift it into covector units by the dual row's Euclidean norm.
\cs_new_protected:Npn \__tenkz_kernel_hull_reach_linear:NnnnN #1#2#3#4#5
  {
    \__tenkz_geom_support_max_linear:nnnN
      { \cs_to_str:N #1 } {#2} {#3} #5
    \tl_set:Ne #5
      {
        \fp_eval:n
          { #5 + (#4) * sqrt( (#2) ^ 2 + (#3) ^ 2 ) }
      }
  }
\tl_new:N \l__tenkz_kernel_r_tau_tl
\tl_new:N \l__tenkz_kernel_r_turnopt_tl

% the hull's support along #2 degrees, padded by #3, over the obstacle seq #1
\cs_new_protected:Npn \__tenkz_kernel_hull_reach:NnnN #1#2#3#4
  {
    \__tenkz_geom_support_max:nnN { \cs_to_str:N #1 } {#2} #4
    \tl_set:Ne #4 { \fp_eval:n { #4 + (#3) } }
  }

% ---------- the cell set a selection stands on --------------------------------
% A selection whose members all stand on frame cells has a shape of its own:
% a staircase when the cells make a staircase, a rectangle only when they
% fill one.  The four supports above answer with the bounding rectangle,
% which is what a bracket wants -- its arc is one side of that rectangle --
% and is a different claim from the one a region contour makes.  A contour
% over cells that do not fill their bounding rectangle therefore traces the
% cells themselves.
%
% The trace runs on the cell lattice, in the local chart the supports were
% folded in.  Between a member and a non-member the boundary stands off the
% MEMBER by the clearance the hull already fixed, not on the midline between
% them, so the four supports settle the four standoffs and a selection that
% does fill its rectangle traces exactly the rectangle it always traced.
% Directed boundary half-edges are enumerated with the member on the left,
% chained into loops on the cell-corner lattice, and every corner placed
% where its two offset lines meet.  A punctured selection closes more than
% one loop, and the even-odd rule fills such a selection as a ring.
%
% Half-edges are keyed by corner AND direction.  At almost every corner one
% strand passes and the chain is forced; where two members meet only at a
% corner, the boundary passes through that point twice, and the saddle rule
% settles the continuation: the member a strand rides stays on its left, so
% the strand turns left, toward that member, and the two strands pass clear
% of one another at the two offset corners the standoffs already separate.
%
% Under the affine chart the east axis is the column direction and the north
% axis is the reverse row direction, so the local abscissa rises with the
% column and the local ordinate falls with the row.  Both facts are read off
% the frame, never assumed: the coordinates below come from the frame map.
\prop_new:N \l__tenkz_kernel_cells_prop
\prop_new:N \l__tenkz_kernel_cells_rowv_prop
\prop_new:N \l__tenkz_kernel_cells_colu_prop
\prop_new:N \l__tenkz_kernel_cells_end_prop
\prop_new:N \l__tenkz_kernel_cells_out_prop
\prop_new:N \l__tenkz_kernel_cells_val_prop
\prop_new:N \l__tenkz_kernel_cells_seen_prop
\seq_new:N \l__tenkz_kernel_cells_starts_seq
\seq_new:N \l__tenkz_kernel_cells_loop_seq
\int_new:N \l__tenkz_kernel_cells_rmin_int
\int_new:N \l__tenkz_kernel_cells_rmax_int
\int_new:N \l__tenkz_kernel_cells_cmin_int
\int_new:N \l__tenkz_kernel_cells_cmax_int
\bool_new:N \l__tenkz_kernel_cells_bool
\bool_new:N \l__tenkz_kernel_cells_rect_bool
\bool_new:N \l__tenkz_kernel_cells_at_bool
\tl_new:N \l__tenkz_kernel_cells_row_tl
\tl_new:N \l__tenkz_kernel_cells_col_tl
\tl_new:N \l__tenkz_kernel_cells_x_tl
\tl_new:N \l__tenkz_kernel_cells_y_tl
\tl_new:N \l__tenkz_kernel_cells_u_tl
\tl_new:N \l__tenkz_kernel_cells_v_tl
\tl_new:N \l__tenkz_kernel_cells_east_tl
\tl_new:N \l__tenkz_kernel_cells_west_tl
\tl_new:N \l__tenkz_kernel_cells_north_tl
\tl_new:N \l__tenkz_kernel_cells_south_tl
\tl_new:N \l__tenkz_kernel_cells_cur_tl
\tl_new:N \l__tenkz_kernel_cells_next_tl
\tl_new:N \l__tenkz_kernel_cells_dir_tl
\tl_new:N \l__tenkz_kernel_cells_out_tl
\tl_new:N \l__tenkz_kernel_cells_o_tl
\tl_new:N \l__tenkz_kernel_cells_val_tl
\tl_new:N \l__tenkz_kernel_cells_po_tl
\tl_new:N \l__tenkz_kernel_cells_pv_tl
\tl_new:N \l__tenkz_kernel_cells_last_tl

\cs_new:Npn \__tenkz_kernel_cells_key:nn #1#2
  { \int_eval:n {#1} - \int_eval:n {#2} }
% a half-edge is a corner #1 and the direction #2 it leaves by
\cs_new:Npn \__tenkz_kernel_cells_ekey:nn #1#2 { #1 . #2 }
% the direction one quarter turn to the left of #1
\cs_new:Npn \__tenkz_kernel_cells_left:n #1
  { \str_case:en {#1} { {E} {N}  {N} {W}  {W} {S}  {S} {E} } }
% the axis a direction runs along
\cs_new:Npn \__tenkz_kernel_cells_axis:n #1
  { \str_case:en {#1} { {E} {H}  {W} {H}  {N} {V}  {S} {V} } }
\prg_new_conditional:Npnn \__tenkz_kernel_cells_member:nn #1#2 { p, T, F }
  {
    \prop_if_in:NeTF \l__tenkz_kernel_cells_prop
      { \__tenkz_kernel_cells_key:nn {#1}{#2} }
      { \prg_return_true: } { \prg_return_false: }
  }
% the local abscissa of a column and the local ordinate of a row
\cs_new:Npn \__tenkz_kernel_cells_u:n #1
  { \prop_item:Ne \l__tenkz_kernel_cells_colu_prop { \int_eval:n {#1} } }
\cs_new:Npn \__tenkz_kernel_cells_v:n #1
  { \prop_item:Ne \l__tenkz_kernel_cells_rowv_prop { \int_eval:n {#1} } }

% The cells a selection names, and whether it is a cell set at all.  A member
% that stands on no frame cell -- a cluster member, a place on a curve --
% leaves the selection without a cell shape, and the bounding rectangle
% remains its contour.
\cs_new_protected:Npn \__tenkz_kernel_hull_cells:N #1
  {
    \prop_clear:N \l__tenkz_kernel_cells_prop
    \bool_set_true:N \l__tenkz_kernel_cells_bool
    \int_set:Nn \l__tenkz_kernel_cells_rmin_int {99999}
    \int_set:Nn \l__tenkz_kernel_cells_rmax_int {-99999}
    \int_set:Nn \l__tenkz_kernel_cells_cmin_int {99999}
    \int_set:Nn \l__tenkz_kernel_cells_cmax_int {-99999}
    \seq_map_inline:Nn #1
      {
        \__tenkz_kernel_atom_rc:nNNN {##1}
          \l__tenkz_kernel_cells_row_tl \l__tenkz_kernel_cells_col_tl
          \l__tenkz_kernel_cells_at_bool
        \bool_if:NTF \l__tenkz_kernel_cells_at_bool
          {
            \prop_put:Nen \l__tenkz_kernel_cells_prop
              {
                \__tenkz_kernel_cells_key:nn
                  { \l__tenkz_kernel_cells_row_tl }
                  { \l__tenkz_kernel_cells_col_tl }
              }
              {1}
            \int_set:Nn \l__tenkz_kernel_cells_rmin_int
              { \int_min:nn { \l__tenkz_kernel_cells_rmin_int }
                  { \l__tenkz_kernel_cells_row_tl } }
            \int_set:Nn \l__tenkz_kernel_cells_rmax_int
              { \int_max:nn { \l__tenkz_kernel_cells_rmax_int }
                  { \l__tenkz_kernel_cells_row_tl } }
            \int_set:Nn \l__tenkz_kernel_cells_cmin_int
              { \int_min:nn { \l__tenkz_kernel_cells_cmin_int }
                  { \l__tenkz_kernel_cells_col_tl } }
            \int_set:Nn \l__tenkz_kernel_cells_cmax_int
              { \int_max:nn { \l__tenkz_kernel_cells_cmax_int }
                  { \l__tenkz_kernel_cells_col_tl } }
          }
          { \bool_set_false:N \l__tenkz_kernel_cells_bool }
      }
    \bool_set_false:N \l__tenkz_kernel_cells_rect_bool
    \bool_if:NT \l__tenkz_kernel_cells_bool
      {
        \int_compare:nNnT { \prop_count:N \l__tenkz_kernel_cells_prop }
          =
          {
            ( \l__tenkz_kernel_cells_rmax_int
              - \l__tenkz_kernel_cells_rmin_int + 1 )
            * ( \l__tenkz_kernel_cells_cmax_int
                - \l__tenkz_kernel_cells_cmin_int + 1 )
          }
          { \bool_set_true:N \l__tenkz_kernel_cells_rect_bool }
      }
  }

% One cell centre in the local chart the supports were folded in.
\cs_new_protected:Npn \__tenkz_kernel_cells_uv:nnNN #1#2#3#4
  {
    \__tenkz_geom_apply:nnnNN {kpic} {#1} {#2}
      \l__tenkz_kernel_cells_x_tl \l__tenkz_kernel_cells_y_tl
    \tl_set:Ne #3
      {
        \fp_eval:n
          {
            ( \l__tenkz_kernel_hull_ux_tl )
              * ( \l__tenkz_kernel_cells_x_tl )
            + ( \l__tenkz_kernel_hull_uy_tl )
              * ( \l__tenkz_kernel_cells_y_tl )
          }
      }
    \tl_set:Ne #4
      {
        \fp_eval:n
          {
            ( \l__tenkz_kernel_hull_vx_tl )
              * ( \l__tenkz_kernel_cells_x_tl )
            + ( \l__tenkz_kernel_hull_vy_tl )
              * ( \l__tenkz_kernel_cells_y_tl )
          }
      }
  }

% The row and column coordinates of the cell shape, and the four standoffs
% between a member's centre and the contour beside it.  The standoffs are
% read off the supports the hull folded, so the contour of a filled
% rectangle is that rectangle.
\cs_new_protected:Npn \__tenkz_kernel_cells_geometry:
  {
    \prop_clear:N \l__tenkz_kernel_cells_rowv_prop
    \prop_clear:N \l__tenkz_kernel_cells_colu_prop
    \int_step_inline:nnn
      { \l__tenkz_kernel_cells_rmin_int } { \l__tenkz_kernel_cells_rmax_int }
      {
        \__tenkz_kernel_cells_uv:nnNN {##1}
          { \l__tenkz_kernel_cells_cmin_int }
          \l__tenkz_kernel_cells_u_tl \l__tenkz_kernel_cells_v_tl
        \prop_put:NnV \l__tenkz_kernel_cells_rowv_prop {##1}
          \l__tenkz_kernel_cells_v_tl
      }
    \int_step_inline:nnn
      { \l__tenkz_kernel_cells_cmin_int } { \l__tenkz_kernel_cells_cmax_int }
      {
        \__tenkz_kernel_cells_uv:nnNN
          { \l__tenkz_kernel_cells_rmin_int } {##1}
          \l__tenkz_kernel_cells_u_tl \l__tenkz_kernel_cells_v_tl
        \prop_put:NnV \l__tenkz_kernel_cells_colu_prop {##1}
          \l__tenkz_kernel_cells_u_tl
      }
    \tl_set:Ne \l__tenkz_kernel_cells_east_tl
      {
        \fp_eval:n
          {
            \l__tenkz_kernel_r_xb_tl
            - ( \__tenkz_kernel_cells_u:n
                  { \l__tenkz_kernel_cells_cmax_int } )
          }
      }
    \tl_set:Ne \l__tenkz_kernel_cells_west_tl
      {
        \fp_eval:n
          {
            ( \__tenkz_kernel_cells_u:n
                { \l__tenkz_kernel_cells_cmin_int } )
            - \l__tenkz_kernel_r_x_tl
          }
      }
    \tl_set:Ne \l__tenkz_kernel_cells_north_tl
      {
        \fp_eval:n
          {
            \l__tenkz_kernel_r_yb_tl
            - ( \__tenkz_kernel_cells_v:n
                  { \l__tenkz_kernel_cells_rmin_int } )
          }
      }
    \tl_set:Ne \l__tenkz_kernel_cells_south_tl
      {
        \fp_eval:n
          {
            ( \__tenkz_kernel_cells_v:n
                { \l__tenkz_kernel_cells_rmax_int } )
            - \l__tenkz_kernel_r_y_tl
          }
      }
  }

% Every boundary half-edge of the cell set, directed with the member on its
% left: outer loops run counter-clockwise and hole loops clockwise, which is
% what lets one even-odd path fill a punctured selection as a ring.
\cs_new_protected:Npn \__tenkz_kernel_cells_edges:
  {
    \prop_clear:N \l__tenkz_kernel_cells_end_prop
    \prop_clear:N \l__tenkz_kernel_cells_out_prop
    \prop_clear:N \l__tenkz_kernel_cells_val_prop
    \seq_clear:N \l__tenkz_kernel_cells_starts_seq
    \int_step_inline:nnn
      { \l__tenkz_kernel_cells_rmin_int - 1 }
      { \l__tenkz_kernel_cells_rmax_int }
      {
        \int_step_inline:nnn
          { \l__tenkz_kernel_cells_cmin_int }
          { \l__tenkz_kernel_cells_cmax_int }
          { \__tenkz_kernel_cells_hedge:nn {##1} {####1} }
      }
    \int_step_inline:nnn
      { \l__tenkz_kernel_cells_rmin_int }
      { \l__tenkz_kernel_cells_rmax_int }
      {
        \int_step_inline:nnn
          { \l__tenkz_kernel_cells_cmin_int - 1 }
          { \l__tenkz_kernel_cells_cmax_int }
          { \__tenkz_kernel_cells_vedge:nn {##1} {####1} }
      }
  }
% the cell edge under row #1 in column #2: a boundary exactly when one of the
% two cells it separates is a member
\cs_new_protected:Npn \__tenkz_kernel_cells_hedge:nn #1#2
  {
    \bool_lazy_and:nnTF
      { \__tenkz_kernel_cells_member_p:nn {#1}{#2} }
      { ! \__tenkz_kernel_cells_member_p:nn { \int_eval:n {#1+1} }{#2} }
      {
        % member to the north: the boundary runs east, one south standoff
        % below that member's centre
        \__tenkz_kernel_cells_edge:nnnn
          { \__tenkz_kernel_cells_key:nn {#1} { \int_eval:n {#2-1} } }
          { \__tenkz_kernel_cells_key:nn {#1} {#2} } {E}
          {
            \fp_eval:n
              {
                ( \__tenkz_kernel_cells_v:n {#1} )
                - \l__tenkz_kernel_cells_south_tl
              }
          }
      }
      {
        \bool_lazy_and:nnT
          { ! \__tenkz_kernel_cells_member_p:nn {#1}{#2} }
          { \__tenkz_kernel_cells_member_p:nn { \int_eval:n {#1+1} }{#2} }
          {
            % member to the south: the boundary runs west, one north standoff
            % above that member's centre
            \__tenkz_kernel_cells_edge:nnnn
              { \__tenkz_kernel_cells_key:nn {#1} {#2} }
              { \__tenkz_kernel_cells_key:nn {#1} { \int_eval:n {#2-1} } } {W}
              {
                \fp_eval:n
                  {
                    ( \__tenkz_kernel_cells_v:n { \int_eval:n {#1+1} } )
                    + \l__tenkz_kernel_cells_north_tl
                  }
              }
          }
      }
  }
% the cell edge east of column #2 in row #1
\cs_new_protected:Npn \__tenkz_kernel_cells_vedge:nn #1#2
  {
    \bool_lazy_and:nnTF
      { \__tenkz_kernel_cells_member_p:nn {#1}{#2} }
      { ! \__tenkz_kernel_cells_member_p:nn {#1} { \int_eval:n {#2+1} } }
      {
        % member to the west: the boundary runs north, one east standoff
        % beyond that member's centre
        \__tenkz_kernel_cells_edge:nnnn
          { \__tenkz_kernel_cells_key:nn {#1} {#2} }
          { \__tenkz_kernel_cells_key:nn { \int_eval:n {#1-1} } {#2} } {N}
          {
            \fp_eval:n
              {
                ( \__tenkz_kernel_cells_u:n {#2} )
                + \l__tenkz_kernel_cells_east_tl
              }
          }
      }
      {
        \bool_lazy_and:nnT
          { ! \__tenkz_kernel_cells_member_p:nn {#1}{#2} }
          { \__tenkz_kernel_cells_member_p:nn {#1} { \int_eval:n {#2+1} } }
          {
            % member to the east: the boundary runs south, one west standoff
            % before that member's centre
            \__tenkz_kernel_cells_edge:nnnn
              { \__tenkz_kernel_cells_key:nn { \int_eval:n {#1-1} } {#2} }
              { \__tenkz_kernel_cells_key:nn {#1} {#2} } {S}
              {
                \fp_eval:n
                  {
                    ( \__tenkz_kernel_cells_u:n { \int_eval:n {#2+1} } )
                    - \l__tenkz_kernel_cells_west_tl
                  }
              }
          }
      }
  }
% One half-edge, from corner #1 in direction #3 (E, W, N or S) to corner #2,
% on the offset line #4.  Keying by corner and direction lets one corner
% carry two departing strands: two selected cells meeting across that corner
% and nowhere else contribute two half-edges in, two out, and each entry
% below stays distinct.
\cs_new_protected:Npn \__tenkz_kernel_cells_edge:nnnn #1#2#3#4
  {
    \prop_put:Nee \l__tenkz_kernel_cells_end_prop
      { \__tenkz_kernel_cells_ekey:nn {#1} {#3} } {#2}
    \prop_put:Nee \l__tenkz_kernel_cells_val_prop
      { \__tenkz_kernel_cells_ekey:nn {#1} {#3} } {#4}
    \prop_put:Nee \l__tenkz_kernel_cells_out_prop {#1}
      { \prop_item:Ne \l__tenkz_kernel_cells_out_prop {#1} #3 }
    \seq_put_right:Ne \l__tenkz_kernel_cells_starts_seq { {#1} {#3} }
  }

% The whole boundary as one path: every loop closed, all of them in the same
% path so the even-odd rule reads them together.
\cs_new_protected:Npn \__tenkz_kernel_cells_path:
  {
    \tl_clear:N \l__tenkz_kernel_r_pts_tl
    \prop_clear:N \l__tenkz_kernel_cells_seen_prop
    \seq_map_inline:Nn \l__tenkz_kernel_cells_starts_seq
      {
        \prop_if_in:NeF \l__tenkz_kernel_cells_seen_prop
          { \__tenkz_kernel_cells_ekey:nn ##1 }
          { \__tenkz_kernel_cells_loop:nn ##1 }
      }
  }
% One closed walk from the half-edge leaving corner #1 in direction #2.  A
% corner with one departing strand forces the chain.  A corner with two is a
% saddle: both members touch it, the one this strand rides is on its left,
% and the left turn is the departure that keeps riding it -- so the strand
% turns toward its member and the two walks pass without joining.
\cs_new_protected:Npn \__tenkz_kernel_cells_loop:nn #1#2
  {
    \seq_clear:N \l__tenkz_kernel_cells_loop_seq
    \tl_set:Nn \l__tenkz_kernel_cells_cur_tl {#1}
    \tl_set:Nn \l__tenkz_kernel_cells_dir_tl {#2}
    \bool_until_do:nn
      {
        \prop_if_in_p:Ne \l__tenkz_kernel_cells_seen_prop
          {
            \__tenkz_kernel_cells_ekey:nn
              { \l__tenkz_kernel_cells_cur_tl }
              { \l__tenkz_kernel_cells_dir_tl }
          }
      }
      {
        \prop_put:Nen \l__tenkz_kernel_cells_seen_prop
          {
            \__tenkz_kernel_cells_ekey:nn
              { \l__tenkz_kernel_cells_cur_tl }
              { \l__tenkz_kernel_cells_dir_tl }
          }
          {1}
        \tl_set:Ne \l__tenkz_kernel_cells_o_tl
          { \__tenkz_kernel_cells_axis:n { \l__tenkz_kernel_cells_dir_tl } }
        \tl_set:Ne \l__tenkz_kernel_cells_val_tl
          {
            \prop_item:Ne \l__tenkz_kernel_cells_val_prop
              {
                \__tenkz_kernel_cells_ekey:nn
                  { \l__tenkz_kernel_cells_cur_tl }
                  { \l__tenkz_kernel_cells_dir_tl }
              }
          }
        \seq_put_right:Ne \l__tenkz_kernel_cells_loop_seq
          {
            { \exp_not:V \l__tenkz_kernel_cells_o_tl }
            { \exp_not:V \l__tenkz_kernel_cells_val_tl }
          }
        \tl_set:Ne \l__tenkz_kernel_cells_next_tl
          {
            \prop_item:Ne \l__tenkz_kernel_cells_end_prop
              {
                \__tenkz_kernel_cells_ekey:nn
                  { \l__tenkz_kernel_cells_cur_tl }
                  { \l__tenkz_kernel_cells_dir_tl }
              }
          }
        \tl_set:Ne \l__tenkz_kernel_cells_out_tl
          {
            \prop_item:Ne \l__tenkz_kernel_cells_out_prop
              { \l__tenkz_kernel_cells_next_tl }
          }
        \int_compare:nNnTF
          { \tl_count:N \l__tenkz_kernel_cells_out_tl } = {2}
          {
            \tl_set:Ne \l__tenkz_kernel_cells_dir_tl
              {
                \__tenkz_kernel_cells_left:n
                  { \l__tenkz_kernel_cells_dir_tl }
              }
          }
          {
            \tl_set_eq:NN \l__tenkz_kernel_cells_dir_tl
              \l__tenkz_kernel_cells_out_tl
          }
        \tl_set_eq:NN \l__tenkz_kernel_cells_cur_tl
          \l__tenkz_kernel_cells_next_tl
      }
    \int_compare:nNnT { \seq_count:N \l__tenkz_kernel_cells_loop_seq } > {0}
      {
        % the previous edge starts as the last one, so the closing corner is
        % emitted on the first turn like every other
        \seq_get_right:NN \l__tenkz_kernel_cells_loop_seq
          \l__tenkz_kernel_cells_last_tl
        \exp_after:wN \__tenkz_kernel_cells_prev:nn
          \l__tenkz_kernel_cells_last_tl
        \seq_map_inline:Nn \l__tenkz_kernel_cells_loop_seq
          { \__tenkz_kernel_cells_corner:nn ##1 }
        \tl_put_right:Nn \l__tenkz_kernel_r_pts_tl { cycle~ }
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_cells_prev:nn #1#2
  {
    \tl_set:Nn \l__tenkz_kernel_cells_po_tl {#1}
    \tl_set:Nn \l__tenkz_kernel_cells_pv_tl {#2}
  }
% A corner stands where the orientation changes: its abscissa comes from the
% vertical edge and its ordinate from the horizontal one.  Collinear edges
% leave the orientation alone and pass without a corner.
\cs_new_protected:Npn \__tenkz_kernel_cells_corner:nn #1#2
  {
    \str_if_eq:VnF \l__tenkz_kernel_cells_po_tl {#1}
      {
        \str_if_eq:nnTF {#1} {V}
          {
            \__tenkz_kernel_cells_corner_ink:nn
              {#2} { \l__tenkz_kernel_cells_pv_tl }
          }
          {
            \__tenkz_kernel_cells_corner_ink:nn
              { \l__tenkz_kernel_cells_pv_tl } {#2}
          }
      }
    \tl_set:Nn \l__tenkz_kernel_cells_po_tl {#1}
    \tl_set:Nn \l__tenkz_kernel_cells_pv_tl {#2}
  }
\cs_new_protected:Npn \__tenkz_kernel_cells_corner_ink:nn #1#2
  {
    \__tenkz_geom_basis_apply:nnnnnnNN
      { \l__tenkz_kernel_hull_ex_tl }
      { \l__tenkz_kernel_hull_ey_tl }
      { \l__tenkz_kernel_hull_nx_tl }
      { \l__tenkz_kernel_hull_ny_tl }
      {#1}{#2}
      \l__tenkz_kernel_ro_apply_x_tl \l__tenkz_kernel_ro_apply_y_tl
    \tl_put_right:Ne \l__tenkz_kernel_r_pts_tl
      {
        \__tenkz_kernel_r_pair:nn
          { \l__tenkz_kernel_ro_apply_x_tl }
          { \l__tenkz_kernel_ro_apply_y_tl }
        --
      }
  }

% ---------- the route that rides the hull -------------------------------------
% A wire whose route is `<side> of <selector>' travels at one standing offset
% from that selection's offset hull, on the named side, entering and leaving
% where that side's arc ends.  Two things follow from the claim and are
% therefore computed, never chosen: the crossing set, which is the wires that
% leave the selection through a face on that side, each met once in hull
% order; and the reach of every leg it crosses, which is the lane it must
% reach plus one clearance.  An empty crossing set is legal and says the
% string passes the selection clear.
%
% The first half of this section answers those questions from the model
% alone, before any measurement, so a derived crossing is written into the
% wire's own crossing field in the spelling a declaration uses and an audit
% cannot tell the two apart.  The second half is the ink.
%
% The route asks its questions inside walks over wires, atoms and legs, so
% like the hull builder it touches none of the shared scratch: these are its
% alone.
\tl_new:N \l__tenkz_kernel_ro_tl
\tl_new:N \l__tenkz_kernel_ro_a_tl
\tl_new:N \l__tenkz_kernel_ro_b_tl
\tl_new:N \l__tenkz_kernel_ro_side_tl
\tl_new:N \l__tenkz_kernel_ro_sel_tl
\tl_new:N \l__tenkz_kernel_ro_row_tl
\tl_new:N \l__tenkz_kernel_ro_col_tl
\tl_new:N \l__tenkz_kernel_ro_sid_tl
\tl_new:N \l__tenkz_kernel_ro_habit_tl
\tl_new:N \l__tenkz_kernel_ro_cross_tl
\tl_new:N \l__tenkz_kernel_ro_lane_tl
\tl_new:N \l__tenkz_kernel_ro_scale_tl
\tl_new:N \l__tenkz_kernel_ro_reach_tl
\seq_new:N \l__tenkz_kernel_ro_leg_seq
\seq_new:N \l__tenkz_kernel_ro_sort_seq
\seq_new:N \l__tenkz_kernel_ro_face_seq
\seq_new:N \l__tenkz_kernel_ro_have_seq
\seq_new:N \l__tenkz_kernel_ro_mem_seq
\seq_new:N \l__tenkz_kernel_ro_obst_seq
\int_new:N \l__tenkz_kernel_ro_int
\bool_new:N \l__tenkz_kernel_ro_bool
\fp_new:N \l__tenkz_kernel_ro_x_fp
\fp_new:N \l__tenkz_kernel_ro_xb_fp
\fp_new:N \l__tenkz_kernel_ro_y_fp
\fp_new:N \l__tenkz_kernel_ro_yb_fp
\fp_new:N \l__tenkz_kernel_ro_u_fp
\fp_new:N \l__tenkz_kernel_ro_v_fp
\fp_new:N \l__tenkz_kernel_ro_cx_fp
\fp_new:N \l__tenkz_kernel_ro_cy_fp
\fp_new:N \l__tenkz_kernel_ro_xmin_fp
\fp_new:N \l__tenkz_kernel_ro_xmax_fp
\fp_new:N \l__tenkz_kernel_ro_ymin_fp
\fp_new:N \l__tenkz_kernel_ro_ymax_fp
\tl_new:N \l__tenkz_kernel_ro_gx_tl
\tl_new:N \l__tenkz_kernel_ro_gy_tl
\tl_new:N \l__tenkz_kernel_ro_ex_tl
\tl_new:N \l__tenkz_kernel_ro_ey_tl
\tl_new:N \l__tenkz_kernel_ro_nx_tl
\tl_new:N \l__tenkz_kernel_ro_ny_tl
\tl_new:N \l__tenkz_kernel_ro_ux_tl
\tl_new:N \l__tenkz_kernel_ro_uy_tl
\tl_new:N \l__tenkz_kernel_ro_vx_tl
\tl_new:N \l__tenkz_kernel_ro_vy_tl
\tl_new:N \l__tenkz_kernel_ro_apply_x_tl
\tl_new:N \l__tenkz_kernel_ro_apply_y_tl
\prop_new:N \l__tenkz_kernel_ro_lane_prop    % wire id -> lane coordinate
\prop_new:N \l__tenkz_kernel_ro_box_prop     % id -> {u}{ub}{v}{vb}{E}{N}
\prop_new:N \l__tenkz_kernel_ro_turn_prop    % wire id/end -> exact hull turn
\prop_new:N \l__tenkz_kernel_ro_legs_prop    % leg id -> {{lane}{gx}{gy}},...
\prop_new:N \l__tenkz_kernel_ro_taken_prop   % side | selector -> next lane
\prop_new:N \l__tenkz_kernel_ro_family_prop  % side | selector -> string ids

% A route stands in the complete E,N basis of the selection it travels.  Its
% box, lane, and endpoint comparisons stay in those coordinates until the
% final carry to the page.  This state boundary preserves shear; a scalar turn
% cannot.
\cs_new_protected:Npn \__tenkz_kernel_ro_basis_apply:NN #1#2
  {
    \__tenkz_geom_basis_apply:nnnnnnNN
      { \l__tenkz_kernel_ro_ex_tl } { \l__tenkz_kernel_ro_ey_tl }
      { \l__tenkz_kernel_ro_nx_tl } { \l__tenkz_kernel_ro_ny_tl }
      {#1}{#2}
      \l__tenkz_kernel_ro_apply_x_tl \l__tenkz_kernel_ro_apply_y_tl
    \fp_set:Nn #1 { \l__tenkz_kernel_ro_apply_x_tl }
    \fp_set:Nn #2 { \l__tenkz_kernel_ro_apply_y_tl }
  }
% a pair already on the page, read in the route's axes
\cs_new_protected:Npn \__tenkz_kernel_ro_basis_unapply:NN #1#2
  {
    \__tenkz_geom_basis_unapply:nnnnnnNN
      { \l__tenkz_kernel_ro_ex_tl } { \l__tenkz_kernel_ro_ey_tl }
      { \l__tenkz_kernel_ro_nx_tl } { \l__tenkz_kernel_ro_ny_tl }
      {#1}{#2}
      \l__tenkz_kernel_ro_apply_x_tl \l__tenkz_kernel_ro_apply_y_tl
    \fp_set:Nn #1 { \l__tenkz_kernel_ro_apply_x_tl }
    \fp_set:Nn #2 { \l__tenkz_kernel_ro_apply_y_tl }
  }

\cs_new_protected:Npn \__tenkz_kernel_route_center:
  {
    \int_zero:N \l__tenkz_kernel_ro_int
    \seq_map_inline:Nn \l__tenkz_kernel_ro_mem_seq
      {
        \__tenkz_kernel_r_xy:nNN {##1}
          \l__tenkz_kernel_r_x_fp \l__tenkz_kernel_r_y_fp
        \__tenkz_kernel_ro_basis_unapply:NN
          \l__tenkz_kernel_r_x_fp \l__tenkz_kernel_r_y_fp
        \int_compare:nNnTF { \l__tenkz_kernel_ro_int } = {0}
          {
            \fp_set_eq:NN
              \l__tenkz_kernel_ro_xmin_fp \l__tenkz_kernel_r_x_fp
            \fp_set_eq:NN
              \l__tenkz_kernel_ro_xmax_fp \l__tenkz_kernel_r_x_fp
            \fp_set_eq:NN
              \l__tenkz_kernel_ro_ymin_fp \l__tenkz_kernel_r_y_fp
            \fp_set_eq:NN
              \l__tenkz_kernel_ro_ymax_fp \l__tenkz_kernel_r_y_fp
          }
          {
            \fp_set:Nn \l__tenkz_kernel_ro_xmin_fp
              {
                min(
                  \l__tenkz_kernel_ro_xmin_fp,
                  \l__tenkz_kernel_r_x_fp
                )
              }
            \fp_set:Nn \l__tenkz_kernel_ro_xmax_fp
              {
                max(
                  \l__tenkz_kernel_ro_xmax_fp,
                  \l__tenkz_kernel_r_x_fp
                )
              }
            \fp_set:Nn \l__tenkz_kernel_ro_ymin_fp
              {
                min(
                  \l__tenkz_kernel_ro_ymin_fp,
                  \l__tenkz_kernel_r_y_fp
                )
              }
            \fp_set:Nn \l__tenkz_kernel_ro_ymax_fp
              {
                max(
                  \l__tenkz_kernel_ro_ymax_fp,
                  \l__tenkz_kernel_r_y_fp
                )
              }
          }
        \int_incr:N \l__tenkz_kernel_ro_int
      }
    \fp_set:Nn \l__tenkz_kernel_ro_cx_fp
      {
        (
          \l__tenkz_kernel_ro_xmin_fp
          + \l__tenkz_kernel_ro_xmax_fp
        ) / 2
      }
    \fp_set:Nn \l__tenkz_kernel_ro_cy_fp
      {
        (
          \l__tenkz_kernel_ro_ymin_fp
          + \l__tenkz_kernel_ro_ymax_fp
        ) / 2
      }
  }

\msg_new:nnn {tenkz}{kernel-route-noroute}
  { [TKZ-ROUTE-NO-ROUTE]~wire~'#1'~has~an~open~end~with~no~direction~and~no~
    route~to~take~its~place~from }
\msg_new:nnn {tenkz}{kernel-route-end-inside}
  { [TKZ-ROUTE-END-INSIDE]~wire~'#1'~has~#2~end~'#3'~inside~its~selected~
    hull;~use~a~port~of~the~selection~or~an~address~outside~the~hull }
\msg_new:nnn {tenkz}{kernel-route-turn-unresolved}
  { [TKZ-ROUTE-TURN-UNRESOLVED]~wire~'#1'~has~an~addressed~all-side~end~
    whose~geometry~did~not~settle~during~topology~resolution }

% route=<side> of <selector> -> the side in #2, the selector in #3; #2 comes
% back empty when the wire's route names a path shape instead of a hull.
\cs_new_protected:Npn \__tenkz_kernel_route_of:nNN #1#2#3
  {
    \tl_clear:N #2
    \tl_clear:N #3
    \__tenkz_model_get:nnN {#1} {route} \l__tenkz_kernel_ro_tl
    \quark_if_no_value:NF \l__tenkz_kernel_ro_tl
      {
        \exp_args:NV \__tenkz_kernel_route_hull_parse:nNNT
          \l__tenkz_kernel_ro_tl #2#3 { }
      }
  }

% Where the arc of side #2 begins (#3 = from) and ends (#3 = to).  A north or
% south arc is travelled west to east and an east or west arc north to south,
% so a wire's two ends are never in doubt and an author who wants the other
% order swaps them.
\cs_new:Npn \__tenkz_kernel_route_end:nn #1#2
  {
    \str_case:nn {#1}
      {
        {n}   { \str_if_eq:nnTF {#2} {from} {w} {e} }
        {s}   { \str_if_eq:nnTF {#2} {from} {w} {e} }
        {all} { \str_if_eq:nnTF {#2} {from} {w} {e} }
        {e}   { \str_if_eq:nnTF {#2} {from} {n} {s} }
        {w}   { \str_if_eq:nnTF {#2} {from} {n} {s} }
      }
  }
\cs_generate_variant:Nn \__tenkz_kernel_route_end:nn { ee }

\prg_new_protected_conditional:Npnn
  \__tenkz_kernel_route_generated_lane:n #1 { T, F, TF }
  {
    \prop_if_in:NeTF \l__tenkz_model_record_prop { #1 / closed }
      { \prg_return_true: }
      {
        \bool_set_true:N \l__tenkz_kernel_ro_bool
        \clist_map_inline:nn {from,to}
          {
            \__tenkz_model_get:nnN {#1} {##1}
              \l__tenkz_kernel_ro_tl
            \quark_if_no_value:NF \l__tenkz_kernel_ro_tl
              { \bool_set_false:N \l__tenkz_kernel_ro_bool }
            \__tenkz_model_get:nnN {#1} {##1-open}
              \l__tenkz_kernel_ro_tl
            \str_if_eq:VnF \l__tenkz_kernel_ro_tl {route}
              { \bool_set_false:N \l__tenkz_kernel_ro_bool }
          }
        \bool_if:NTF \l__tenkz_kernel_ro_bool
          { \prg_return_true: } { \prg_return_false: }
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_route_endpoint_turn:nnN #1#2#3
  {
    \prop_get:NnN \l__tenkz_kernel_ro_turn_prop { #1 / #2 } #3
    \quark_if_no_value:NT #3
      {
        \__tenkz_model_get:nnN {#1} {#2} \l__tenkz_kernel_ro_tl
        \quark_if_no_value:NTF \l__tenkz_kernel_ro_tl
          { \tl_set:Nn #3 {0} }
          { \tl_set:Nn #3 {pending} }
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_route_faces_between:nnN #1#2#3
  {
    \seq_clear:N #3
    \int_set:Nn \l__tenkz_kernel_ro_int
      { \int_mod:nn { #2 - #1 + 4 } {4} }
    \int_step_inline:nn { \l__tenkz_kernel_ro_int }
      {
        \seq_put_right:Ne #3
          {
            \int_case:nn
              { \int_mod:nn { #1 + ##1 - 2 } {4} + 1 }
              { {1}{n} {2}{w} {3}{s} {4}{e} }
          }
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_route_all_faces:nN #1#2
  {
    \prop_if_in:NeTF \l__tenkz_model_record_prop { #1 / closed }
      { \seq_set_from_clist:Nn #2 {n,w,s,e} }
      {
        \__tenkz_kernel_route_endpoint_turn:nnN {#1} {from}
          \l__tenkz_kernel_ro_row_tl
        \__tenkz_kernel_route_endpoint_turn:nnN {#1} {to}
          \l__tenkz_kernel_ro_col_tl
        \bool_lazy_or:nnTF
          { \str_if_eq_p:Vn \l__tenkz_kernel_ro_row_tl {pending} }
          { \str_if_eq_p:Vn \l__tenkz_kernel_ro_col_tl {pending} }
          { \seq_clear:N #2 }
          {
            \int_compare:nNnT { \l__tenkz_kernel_ro_row_tl } = {0}
              { \tl_set:Nn \l__tenkz_kernel_ro_row_tl {1} }
            \int_compare:nNnT { \l__tenkz_kernel_ro_col_tl } = {0}
              { \tl_set:Nn \l__tenkz_kernel_ro_col_tl {4} }
            \exp_args:NVV \__tenkz_kernel_route_faces_between:nnN
              \l__tenkz_kernel_ro_row_tl
              \l__tenkz_kernel_ro_col_tl #2
          }
      }
  }

% The crossing set of wire #1, in hull order, as
% {operand-id}{row}{col}{face}{declaration-operand}.  Policy legs and semantic
% open-port wires answer through one boundary-exit query.  The last field keeps
% the author's stable spelling separate from the internal topology id.
\cs_new_protected:Npn \__tenkz_kernel_route_legs:nN #1#2
  {
    \seq_clear:N #2
    \__tenkz_kernel_route_of:nNN {#1}
      \l__tenkz_kernel_ro_side_tl \l__tenkz_kernel_ro_sel_tl
    \tl_if_empty:NF \l__tenkz_kernel_ro_side_tl
      {
        \seq_clear:N \l__tenkz_kernel_ro_sort_seq
        \__tenkz_kernel_select:VN \l__tenkz_kernel_ro_sel_tl
          \l__tenkz_kernel_ro_mem_seq
        \__tenkz_kernel_hull_basis:NNNNN
          \l__tenkz_kernel_ro_mem_seq
          \l__tenkz_kernel_ro_ex_tl \l__tenkz_kernel_ro_ey_tl
          \l__tenkz_kernel_ro_nx_tl \l__tenkz_kernel_ro_ny_tl
        \__tenkz_geom_basis_dual:nnnnNNNN
          { \l__tenkz_kernel_ro_ex_tl }
          { \l__tenkz_kernel_ro_ey_tl }
          { \l__tenkz_kernel_ro_nx_tl }
          { \l__tenkz_kernel_ro_ny_tl }
          \l__tenkz_kernel_ro_ux_tl \l__tenkz_kernel_ro_uy_tl
          \l__tenkz_kernel_ro_vx_tl \l__tenkz_kernel_ro_vy_tl
        \str_case:Vn \l__tenkz_kernel_ro_side_tl
          {
            {n}   { \__tenkz_kernel_route_face:n {n} }
            {s}   { \__tenkz_kernel_route_face:n {s} }
            {e}   { \__tenkz_kernel_route_face:n {e} }
            {w}   { \__tenkz_kernel_route_face:n {w} }
            {all}
              {
                \__tenkz_kernel_route_center:
                \__tenkz_kernel_route_all_faces:nN {#1}
                  \l__tenkz_kernel_ro_face_seq
                \seq_map_inline:Nn \l__tenkz_kernel_ro_face_seq
                  { \__tenkz_kernel_route_face:n {##1} }
              }
          }
        % hull order: the order the string meets them travelling the arc,
        % represented as a side rank followed by two geometric grid
        % coordinates.  Compare the tuple directly: no grid-size sentinel is
        % allowed to turn two distinct exits into the same scalar key.
        \seq_sort:Nn \l__tenkz_kernel_ro_sort_seq
          {
            \fp_compare:nNnTF
              { \tl_item:nn {##1} {1} } > { \tl_item:nn {##2} {1} }
              { \sort_return_swapped: }
              {
                \fp_compare:nNnTF
                  { \tl_item:nn {##1} {1} } < { \tl_item:nn {##2} {1} }
                  { \sort_return_same: }
                  {
                    \fp_compare:nNnTF
                      { \tl_item:nn {##1} {2} } > { \tl_item:nn {##2} {2} }
                      { \sort_return_swapped: }
                      {
                        \fp_compare:nNnTF
                          { \tl_item:nn {##1} {2} }
                          < { \tl_item:nn {##2} {2} }
                          { \sort_return_same: }
                          {
                            \fp_compare:nNnTF
                              { \tl_item:nn {##1} {3} }
                              > { \tl_item:nn {##2} {3} }
                              { \sort_return_swapped: }
                              { \sort_return_same: }
                          }
                      }
                  }
              }
          }
        \seq_map_inline:Nn \l__tenkz_kernel_ro_sort_seq
          {
            \seq_put_right:Ne #2
              {
                { \tl_item:nn {##1} {4} }
                { \tl_item:nn {##1} {7} }
                { \tl_item:nn {##1} {8} }
                { \tl_item:nn {##1} {5} }
                { \tl_item:nn {##1} {6} }
              }
          }
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_route_page_bearing_side:nN #1#2
  {
    \fp_set:Nn \l__tenkz_kernel_ro_x_fp
      { cosd( #1 ) }
    \fp_set:Nn \l__tenkz_kernel_ro_y_fp
      { sind( #1 ) }
    \__tenkz_kernel_ro_basis_unapply:NN
      \l__tenkz_kernel_ro_x_fp \l__tenkz_kernel_ro_y_fp
    \tl_set:Ne \l__tenkz_kernel_port_face_tl
      {
        \fp_eval:n
          {
            atand(
              \l__tenkz_kernel_ro_y_fp,
              \l__tenkz_kernel_ro_x_fp
            )
          }
      }
    \exp_args:NV \__tenkz_kernel_r_face_side:n
      \l__tenkz_kernel_port_face_tl
    \tl_set_eq:NN #2 \l__tenkz_kernel_r_face_side_tl
  }

\cs_new_protected:Npn \__tenkz_kernel_route_host_face_side:nnN #1#2#3
  {
    \__tenkz_kernel_r_page_face:nnN {#1} {#2}
      \l__tenkz_kernel_port_face_tl
    \exp_args:NV \__tenkz_kernel_route_page_bearing_side:nN
      \l__tenkz_kernel_port_face_tl #3
  }

\cs_new_protected:Npn \__tenkz_kernel_route_policy_face_add:nnn #1#2#3
  {
    \__tenkz_kernel_policy_leg_map:nnn {#1} {#2}
      {
        \__tenkz_kernel_policy_slot_bearing:nnnN {#1} {#2} {##1}
          \l__tenkz_kernel_r_leg_turn_tl
        \exp_args:NV \__tenkz_kernel_route_page_bearing_side:nN
          \l__tenkz_kernel_r_leg_turn_tl
          \l__tenkz_kernel_port_face_tl
        \str_if_eq:VnT \l__tenkz_kernel_port_face_tl {#3}
          { \__tenkz_kernel_route_face_add:nnn {#1} {#3} {##1} }
      }
  }

% Every selected policy leg and semantic open port that exits through face #1.
\cs_new_protected:Npn \__tenkz_kernel_route_face:n #1
  {
    \seq_map_inline:Nn \l__tenkz_kernel_ro_mem_seq
      {
        \__tenkz_kernel_policy_faces:nn {##1}
          {
            \__tenkz_kernel_route_policy_face_add:nnn
              {##1} {####1} {#1}
          }
      }
    \__tenkz_model_map_ids:nn {wire}
      {
        \__tenkz_model_get:nnN {##1} {origin} \l__tenkz_kernel_ro_tl
        \str_if_eq:VnT \l__tenkz_kernel_ro_tl {port-open}
          {
            \__tenkz_model_get:nnN {##1} {host} \l__tenkz_kernel_ro_b_tl
            \seq_if_in:NVTF
              \l__tenkz_kernel_ro_mem_seq \l__tenkz_kernel_ro_b_tl
              {
                \__tenkz_model_get:nnN
                  {##1} {port-face} \l__tenkz_kernel_port_face_tl
                \exp_args:NVV \__tenkz_kernel_route_host_face_side:nnN
                  \l__tenkz_kernel_ro_b_tl
                  \l__tenkz_kernel_port_face_tl
                  \l__tenkz_kernel_port_face_tl
                \str_if_eq:VnT \l__tenkz_kernel_port_face_tl {#1}
                  {
                    \__tenkz_kernel_r_sid:nN {##1}
                      \l__tenkz_kernel_r_sid_tl
                    \exp_args:NVV
                      \__tenkz_kernel_route_port_target_add:nnnn
                      \l__tenkz_kernel_ro_b_tl
                      \l__tenkz_kernel_port_face_tl
                      {##1}
                      { \tl_use:N \l__tenkz_kernel_r_sid_tl }
                  }
              }
              { }
          }
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_route_face_add:nnn #1#2#3
  {
    % an ellipsis occupies its cell and grows no leg
    \__tenkz_model_get:nnN {#1} {skin} \l__tenkz_kernel_ro_b_tl
    \str_if_eq:VnF \l__tenkz_kernel_ro_b_tl {dots}
      {
        \__tenkz_kernel_atom_rc:nNNN {#1}
          \l__tenkz_kernel_ro_row_tl \l__tenkz_kernel_ro_col_tl
          \l__tenkz_kernel_ro_bool
        \bool_if:NT \l__tenkz_kernel_ro_bool
          {
            % the leg of slot #3 leaves the spanned cell #3 - 1 columns east
            \tl_set:Ne \l__tenkz_kernel_ro_col_tl
              { \int_eval:n { \l__tenkz_kernel_ro_col_tl + #3 - 1 } }
            \tl_set:Ne \l__tenkz_kernel_ro_tl
              {
                leg - #2 - \tl_use:N \l__tenkz_kernel_ro_row_tl
                - \tl_use:N \l__tenkz_kernel_ro_col_tl
              }
            \tl_set:Ne \l__tenkz_kernel_ro_b_tl
              {
                leg ~ #2 ~ of ~
                {
                  ( \tl_use:N \l__tenkz_kernel_ro_row_tl ,
                    \tl_use:N \l__tenkz_kernel_ro_col_tl )
                }
              }
            \__tenkz_kernel_route_target_add:nnVVee
              {#1} {#2} \l__tenkz_kernel_ro_tl \l__tenkz_kernel_ro_b_tl
              {
                \str_case:nnF {#2}
                  {
                    {e}{ \tl_use:N \l__tenkz_kernel_ro_row_tl }
                    {w}{ \tl_use:N \l__tenkz_kernel_ro_row_tl }
                  }
                  { \tl_use:N \l__tenkz_kernel_ro_col_tl }
              }
              {
                \str_case:nnF {#2}
                  {
                    {e}{ \tl_use:N \l__tenkz_kernel_ro_col_tl }
                    {w}{ \tl_use:N \l__tenkz_kernel_ro_col_tl }
                  }
                  { \tl_use:N \l__tenkz_kernel_ro_row_tl }
              }
          }
      }
  }
\cs_new_protected:Npn
  \__tenkz_kernel_route_port_target_add:nnnn #1#2#3#4
  {
    \__tenkz_kernel_atom_rc:nNNN {#1}
      \l__tenkz_kernel_ro_row_tl \l__tenkz_kernel_ro_col_tl
      \l__tenkz_kernel_ro_bool
    \bool_if:NTF \l__tenkz_kernel_ro_bool
      {
        \__tenkz_kernel_r_span:nNN {#1}
          \l__tenkz_kernel_ro_b_tl \l__tenkz_kernel_ro_tl
        \__tenkz_model_get:nnN {#3} {port-slot}
          \l__tenkz_kernel_port_slot_tl
        \__tenkz_kernel_route_target_add:nnnnnn
          {#1} {#2} {#4} {#4}
          {
            \str_case:nnF {#2}
              {
                {e}
                  {
                    \l__tenkz_kernel_ro_row_tl
                    + \l__tenkz_kernel_port_slot_tl - 1
                  }
                {w}
                  {
                    \l__tenkz_kernel_ro_row_tl
                    + \l__tenkz_kernel_port_slot_tl - 1
                  }
              }
              {
                \l__tenkz_kernel_ro_col_tl
                + \l__tenkz_kernel_port_slot_tl - 1
              }
          }
          {
            \str_case:nn {#2}
              {
                {n}{ \l__tenkz_kernel_ro_row_tl }
                {s}
                  {
                    \l__tenkz_kernel_ro_row_tl
                    + \l__tenkz_kernel_ro_tl - 1
                  }
                {e}
                  {
                    \l__tenkz_kernel_ro_col_tl
                    + \l__tenkz_kernel_ro_b_tl - 1
                  }
                {w}{ \l__tenkz_kernel_ro_col_tl }
              }
          }
      }
      {
        % A derived carrier has no integer cell, but its resolved port still
        % has one logical position in the picture frame.  The port's slot
        % walks the span symmetrically about that centre, at the same cell
        % step a frame-anchored spanning atom gives its slots, so a route
        % meets one distinct exit per slot here too.
        \__tenkz_kernel_r_span:nNN {#1}
          \l__tenkz_kernel_ro_b_tl \l__tenkz_kernel_ro_tl
        \__tenkz_model_get:nnN {#3} {port-slot}
          \l__tenkz_kernel_port_slot_tl
        \quark_if_no_value:NT \l__tenkz_kernel_port_slot_tl
          { \tl_set:Nn \l__tenkz_kernel_port_slot_tl {1} }
        \__tenkz_kernel_r_xy:nNN {#1}
          \l__tenkz_kernel_r_x_fp \l__tenkz_kernel_r_y_fp
        \__tenkz_geom_unapply:nnnNN {kpic}
          { \l__tenkz_kernel_r_x_fp } { \l__tenkz_kernel_r_y_fp }
          \l__tenkz_kernel_r_xb_fp \l__tenkz_kernel_r_yb_fp
        \tl_set:Ne \l__tenkz_kernel_ro_row_tl
          { \fp_eval:n { \l__tenkz_kernel_r_xb_fp } }
        \tl_set:Ne \l__tenkz_kernel_ro_col_tl
          { \fp_eval:n { \l__tenkz_kernel_r_yb_fp } }
        \__tenkz_kernel_route_target_add:nnnnnn
          {#1} {#2} {#4} {#4}
          {
            \str_case:nnF {#2}
              {
                {e}
                  {
                    \l__tenkz_kernel_ro_row_tl
                    + \l__tenkz_kernel_port_slot_tl
                    - ( \l__tenkz_kernel_ro_tl + 1 ) / 2
                  }
                {w}
                  {
                    \l__tenkz_kernel_ro_row_tl
                    + \l__tenkz_kernel_port_slot_tl
                    - ( \l__tenkz_kernel_ro_tl + 1 ) / 2
                  }
              }
              {
                \l__tenkz_kernel_ro_col_tl
                + \l__tenkz_kernel_port_slot_tl
                - ( \l__tenkz_kernel_ro_b_tl + 1 ) / 2
              }
          }
          {
            \str_case:nn {#2}
              {
                {n}
                  {
                    \l__tenkz_kernel_ro_row_tl
                    - ( \l__tenkz_kernel_ro_tl - 1 ) / 2
                  }
                {s}
                  {
                    \l__tenkz_kernel_ro_row_tl
                    + ( \l__tenkz_kernel_ro_tl - 1 ) / 2
                  }
                {e}
                  {
                    \l__tenkz_kernel_ro_col_tl
                    + ( \l__tenkz_kernel_ro_b_tl - 1 ) / 2
                  }
                {w}
                  {
                    \l__tenkz_kernel_ro_col_tl
                    - ( \l__tenkz_kernel_ro_b_tl - 1 ) / 2
                  }
              }
          }
      }
  }
\cs_new_protected:Npn
  \__tenkz_kernel_route_target_add:nnnnnn #1#2#3#4#5#6
  {
    \tl_set:Nn \l__tenkz_kernel_ro_a_tl {0}
    \str_if_eq:VnT \l__tenkz_kernel_ro_side_tl {all}
      {
        \seq_map_indexed_inline:Nn \l__tenkz_kernel_ro_face_seq
          {
            \str_if_eq:nnT {##2} {#2}
              {
                \tl_set:Nn \l__tenkz_kernel_ro_a_tl {##1}
                \seq_map_break:
              }
          }
      }
    \seq_put_right:Ne \l__tenkz_kernel_ro_sort_seq
          {
            {
              \tl_use:N \l__tenkz_kernel_ro_a_tl
            }
            {
              \str_if_eq:VnTF \l__tenkz_kernel_ro_side_tl {all}
                {
                  \str_case:nnF {#2}
                    { {n}{-(#5)} {e}{-(#5)} }
                    {#5}
                }
                {#5}
            }
            {#6}
            {#3}
            {#2}
            {#4}
            { \tl_use:N \l__tenkz_kernel_ro_row_tl }
            { \tl_use:N \l__tenkz_kernel_ro_col_tl }
          }
  }
\cs_generate_variant:Nn
  \__tenkz_kernel_route_target_add:nnnnnn { nnVVee }

% The habit resolves the crossing set.  Every crossing the route makes is
% written into the wire's own crossing field, in the spelling a declaration
% uses, unless the author already declared that one -- the exception stands,
% and it still counts a step of an alternation, because the order is the
% order the string meets them and not the order it was told about them.  With
% no habit and no exception the crossing stays unresolved and the crossing
% police refuses the picture, which is the guarantee this key was never
% allowed to weaken.
\cs_new_protected:Npn \__tenkz_kernel_route_crossings:
  {
    \__tenkz_model_map_ids:nn {wire}
      { \__tenkz_kernel_route_turns_validate:n {##1} }
    \__tenkz_model_map_ids:nn {wire}
      {
        \__tenkz_kernel_route_open_ends:n {##1}
        \__tenkz_kernel_route_legs:nN {##1} \l__tenkz_kernel_ro_leg_seq
        \seq_if_empty:NF \l__tenkz_kernel_ro_leg_seq
          { \__tenkz_kernel_route_habit:n {##1} }
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_route_turns_validate:n #1
  {
    \__tenkz_kernel_route_of:nNN {#1}
      \l__tenkz_kernel_ro_side_tl \l__tenkz_kernel_ro_sel_tl
    \str_if_eq:VnT \l__tenkz_kernel_ro_side_tl {all}
      {
        \clist_map_inline:nn {from,to}
          {
            \__tenkz_model_get:nnN {#1} {##1} \l__tenkz_kernel_ro_tl
            \quark_if_no_value:NF \l__tenkz_kernel_ro_tl
              {
                \prop_if_in:NnF
                  \l__tenkz_kernel_ro_turn_prop { #1 / ##1 }
                  {
                    \msg_error:nnn
                      {tenkz}{kernel-route-turn-unresolved} {#1}
                  }
              }
          }
      }
  }
% an end with no direction and no route has nowhere to stand
\cs_new_protected:Npn \__tenkz_kernel_route_open_ends:n #1
  {
    \clist_map_inline:nn {from, to}
      {
        \__tenkz_model_get:nnN {#1} {##1-open} \l__tenkz_kernel_ro_b_tl
        \quark_if_no_value:NF \l__tenkz_kernel_ro_b_tl
          {
            \str_if_eq:VnT \l__tenkz_kernel_ro_b_tl {route}
              {
                \__tenkz_kernel_route_of:nNN {#1}
                  \l__tenkz_kernel_ro_side_tl \l__tenkz_kernel_ro_sel_tl
                \tl_if_empty:NT \l__tenkz_kernel_ro_side_tl
                  { \msg_error:nnn {tenkz}{kernel-route-noroute} {#1} }
              }
          }
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_route_habit:n #1
  {
    \__tenkz_model_get:nnN {#1} {crossing} \l__tenkz_kernel_ro_habit_tl
    \quark_if_no_value:NF \l__tenkz_kernel_ro_habit_tl
      {
        \__tenkz_kernel_route_declared:n {#1}
        \__tenkz_kernel_r_sid:nN {#1} \l__tenkz_kernel_ro_sid_tl
        \__tenkz_model_get:nnN {#1} {cross} \l__tenkz_kernel_ro_cross_tl
        \quark_if_no_value:NT \l__tenkz_kernel_ro_cross_tl
          { \tl_clear:N \l__tenkz_kernel_ro_cross_tl }
        \int_zero:N \l__tenkz_kernel_ro_int
        \seq_map_inline:Nn \l__tenkz_kernel_ro_leg_seq
          { \__tenkz_kernel_route_habit_one:nn {##1} {#1} }
        \__tenkz_model_complete:nnn {#1} {cross}
          { \exp_not:V \l__tenkz_kernel_ro_cross_tl }
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_route_habit_one:nn #1#2
  {
    \int_incr:N \l__tenkz_kernel_ro_int
    \tl_set:Ne \l__tenkz_kernel_ro_b_tl { \tl_item:nn {#1} {1} }
    \seq_if_in:NVF \l__tenkz_kernel_ro_have_seq \l__tenkz_kernel_ro_b_tl
      {
        \__tenkz_kernel_route_order:NN
          \l__tenkz_kernel_ro_habit_tl \l__tenkz_kernel_ro_tl
        \tl_if_empty:NF \l__tenkz_kernel_ro_cross_tl
          { \tl_put_right:Nn \l__tenkz_kernel_ro_cross_tl {,} }
        \tl_put_right:Ne \l__tenkz_kernel_ro_cross_tl
          {
            \tl_use:N \l__tenkz_kernel_ro_tl \c_space_tl at ~ crossing ~ of ~
            \tl_use:N \l__tenkz_kernel_ro_sid_tl \c_space_tl and ~
            \tl_item:nn {#1} {5}
          }
      }
  }
% The habit's word at the crossing now being counted.  An alternation flips
% along the string's own order and its anchor names the first.
\cs_new_protected:Npn \__tenkz_kernel_route_order:NN #1#2
  {
    \str_case:VnF #1
      {
        {over}  { \tl_set:Nn #2 {over} }
        {under} { \tl_set:Nn #2 {under} }
      }
      {
        % an alternation's anchor names its first crossing, and every
        % crossing after it takes the other word
        \str_if_eq:VnTF #1 {alternate=under}
          { \tl_set:Nn #2 {under} }
          { \tl_set:Nn #2 {over} }
        \int_if_even:nT { \l__tenkz_kernel_ro_int }
          {
            \str_if_eq:VnTF #2 {over}
              { \tl_set:Nn #2 {under} }
              { \tl_set:Nn #2 {over} }
          }
      }
  }
% The generated leg ids that this route's own complete crossing operands
% already name.  Parsing whole operands avoids treating an identifier that
% merely contains `port-open-N` as that generated wire.
\cs_new_protected:Npn \__tenkz_kernel_route_declared:n #1
  {
    \seq_clear:N \l__tenkz_kernel_ro_have_seq
    \__tenkz_model_get:nnN {#1} {cross} \l__tenkz_kernel_ro_tl
    \quark_if_no_value:NF \l__tenkz_kernel_ro_tl
      {
        \tl_set_eq:NN \l__tenkz_kernel_r_sid_tl
          \l__tenkz_kernel_ro_sid_tl
        \exp_args:NV \clist_map_inline:nn \l__tenkz_kernel_ro_tl
          { \__tenkz_kernel_route_declared_one:n {##1} }
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_route_declared_one:n #1
  {
    \regex_extract_once:nnNT
      {
        \A \s* (over|under) \s+ at \s+ crossing \s+ of \s+ (.+)
        \s+ and \s+ (.+) \s* \Z
      }
      {#1} \l__tenkz_kernel_match_seq
      {
        \tl_set:Ne \l__tenkz_kernel_ro_a_tl
          { \seq_item:Nn \l__tenkz_kernel_match_seq {3} }
        \tl_set:Ne \l__tenkz_kernel_ro_b_tl
          { \seq_item:Nn \l__tenkz_kernel_match_seq {4} }
        \exp_args:NV \__tenkz_kernel_r_crossid:nN
          \l__tenkz_kernel_ro_a_tl \l__tenkz_kernel_r_b_tl
        \exp_args:NV \__tenkz_kernel_r_crossid:nN
          \l__tenkz_kernel_ro_b_tl \l__tenkz_kernel_r_c_tl
        \tl_clear:N \l__tenkz_kernel_ro_a_tl
        \str_if_eq:VVTF
          \l__tenkz_kernel_r_b_tl \l__tenkz_kernel_ro_sid_tl
          {
            \tl_set_eq:NN \l__tenkz_kernel_ro_a_tl
              \l__tenkz_kernel_r_c_tl
          }
          {
            \str_if_eq:VVT
              \l__tenkz_kernel_r_c_tl \l__tenkz_kernel_ro_sid_tl
              {
                \tl_set_eq:NN \l__tenkz_kernel_ro_a_tl
                  \l__tenkz_kernel_r_b_tl
              }
          }
        \exp_args:NnV \regex_match:nnT
          { \A (?:leg-[nesw]-\d+-\d+|port-open-[1-9]\d*) \Z }
          \l__tenkz_kernel_ro_a_tl
          {
            \seq_if_in:NVF
              \l__tenkz_kernel_ro_have_seq \l__tenkz_kernel_ro_a_tl
              {
                \seq_put_right:NV
                  \l__tenkz_kernel_ro_have_seq \l__tenkz_kernel_ro_a_tl
              }
          }
      }
  }

% ---------- the route as ink --------------------------------------------------
% The lane.  A curve over a selection stands one clearance outside that
% selection's offset hull, and curves over the same selection step outward in
% declaration order on each side, so two routes on one side never overlap and
% a route on the opposite side does not consume a lane the author never sees.
%
% The four edges are taken along the selection's own axes, and the side words
% below therefore name a side OF THAT SELECTION: a north lane stands off the
% hull's north wherever that north leans.  The lane is the coordinate of the
% edge it stands on in those axes, which is what makes it a distance a leg can
% be asked to reach; a lane scalar without the axes it was measured in is not
% a distance at all.
\cs_new_protected:Npn \__tenkz_kernel_r_route_lane_take:
  {
    \str_if_eq:VnTF \l__tenkz_kernel_ro_side_tl {all}
      {
        % An all-side route occupies every side at one common offset.  Stand
        % outside the busiest side, then advance all four counters so a later
        % compass-side route cannot land on one of the same four paths.
        \int_zero:N \l__tenkz_kernel_ro_int
        \clist_map_inline:nn {n,s,e,w}
          {
            \prop_get:NeN \l__tenkz_kernel_ro_taken_prop
              { ##1 | \tl_use:N \l__tenkz_kernel_ro_sel_tl }
              \l__tenkz_kernel_ro_lane_tl
            \quark_if_no_value:NF \l__tenkz_kernel_ro_lane_tl
              {
                \int_compare:nNnT
                  { \l__tenkz_kernel_ro_lane_tl }
                  >
                  { \l__tenkz_kernel_ro_int }
                  {
                    \int_set:Nn \l__tenkz_kernel_ro_int
                      { \l__tenkz_kernel_ro_lane_tl }
                  }
              }
          }
        \tl_set:Ne \l__tenkz_kernel_ro_lane_tl
          { \int_use:N \l__tenkz_kernel_ro_int }
        \clist_map_inline:nn {n,s,e,w}
          {
            \prop_put:Nee \l__tenkz_kernel_ro_taken_prop
              { ##1 | \tl_use:N \l__tenkz_kernel_ro_sel_tl }
              { \int_eval:n { \l__tenkz_kernel_ro_int + 1 } }
          }
      }
      {
        \prop_get:NeN \l__tenkz_kernel_ro_taken_prop
          {
            \tl_use:N \l__tenkz_kernel_ro_side_tl
            | \tl_use:N \l__tenkz_kernel_ro_sel_tl
          }
          \l__tenkz_kernel_ro_lane_tl
        \quark_if_no_value:NT \l__tenkz_kernel_ro_lane_tl
          { \tl_set:Nn \l__tenkz_kernel_ro_lane_tl {0} }
        \prop_put:Nee \l__tenkz_kernel_ro_taken_prop
          {
            \tl_use:N \l__tenkz_kernel_ro_side_tl
            | \tl_use:N \l__tenkz_kernel_ro_sel_tl
          }
          { \int_eval:n { \l__tenkz_kernel_ro_lane_tl + 1 } }
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_r_route_lane_enrol:nn #1#2
  {
    \prop_get:NeN \l__tenkz_kernel_ro_family_prop
      { #1 | \tl_use:N \l__tenkz_kernel_ro_sel_tl }
      \l__tenkz_kernel_ro_tl
    \quark_if_no_value:NTF \l__tenkz_kernel_ro_tl
      {
        \prop_put:Nen \l__tenkz_kernel_ro_family_prop
          { #1 | \tl_use:N \l__tenkz_kernel_ro_sel_tl } {#2}
      }
      {
        \exp_args:NV \clist_map_inline:nn \l__tenkz_kernel_ro_tl
          { \__tenkz_string_disjoint:nn {##1} {#2} }
        \tl_put_right:Nn \l__tenkz_kernel_ro_tl {,#2}
        \prop_put:NeV \l__tenkz_kernel_ro_family_prop
          { #1 | \tl_use:N \l__tenkz_kernel_ro_sel_tl }
          \l__tenkz_kernel_ro_tl
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_r_route_measure:n #1
  {
    \__tenkz_kernel_route_of:nNN {#1}
      \l__tenkz_kernel_ro_side_tl \l__tenkz_kernel_ro_sel_tl
    \tl_if_empty:NF \l__tenkz_kernel_ro_side_tl
      {
        \__tenkz_kernel_select:VN \l__tenkz_kernel_ro_sel_tl
          \l__tenkz_kernel_ro_mem_seq
        \__tenkz_kernel_hull_obstacles:NN
          \l__tenkz_kernel_ro_mem_seq \l__tenkz_kernel_ro_obst_seq
        \__tenkz_kernel_hull_scale:NN
          \l__tenkz_kernel_ro_mem_seq \l__tenkz_kernel_ro_scale_tl
        \__tenkz_kernel_hull_basis:NNNNN
          \l__tenkz_kernel_ro_mem_seq
          \l__tenkz_kernel_ro_ex_tl \l__tenkz_kernel_ro_ey_tl
          \l__tenkz_kernel_ro_nx_tl \l__tenkz_kernel_ro_ny_tl
        \__tenkz_geom_basis_dual:nnnnNNNN
          { \l__tenkz_kernel_ro_ex_tl } { \l__tenkz_kernel_ro_ey_tl }
          { \l__tenkz_kernel_ro_nx_tl } { \l__tenkz_kernel_ro_ny_tl }
          \l__tenkz_kernel_ro_ux_tl \l__tenkz_kernel_ro_uy_tl
          \l__tenkz_kernel_ro_vx_tl \l__tenkz_kernel_ro_vy_tl
        % Only the complete generated lane is model-proven disjoint from its
        % siblings.  An address-bearing route adds arbitrary segments between
        % its endpoints and the hull, and those segments still need geometric
        % crossing checks.
        \__tenkz_kernel_route_generated_lane:nT {#1}
          {
            \__tenkz_kernel_r_sid:nN {#1} \l__tenkz_kernel_r_sid_tl
            \str_if_eq:VnTF \l__tenkz_kernel_ro_side_tl {all}
              {
                \clist_map_inline:nn {n,s,e,w}
                  {
                    \exp_args:NnV \__tenkz_kernel_r_route_lane_enrol:nn {##1}
                      \l__tenkz_kernel_r_sid_tl
                  }
              }
              {
                \exp_args:NVV \__tenkz_kernel_r_route_lane_enrol:nn
                  \l__tenkz_kernel_ro_side_tl \l__tenkz_kernel_r_sid_tl
              }
          }
        \__tenkz_kernel_r_route_lane_take:
        \__tenkz_kernel_r_route_edge:nnnN
          { \l__tenkz_kernel_ro_ux_tl }
          { \l__tenkz_kernel_ro_uy_tl } { } \l__tenkz_kernel_ro_xb_fp
        \__tenkz_kernel_r_route_edge:nnnN
          { \l__tenkz_kernel_ro_vx_tl }
          { \l__tenkz_kernel_ro_vy_tl } { } \l__tenkz_kernel_ro_yb_fp
        \__tenkz_kernel_r_route_edge:nnnN
          { -\l__tenkz_kernel_ro_ux_tl }
          { -\l__tenkz_kernel_ro_uy_tl } {-} \l__tenkz_kernel_ro_x_fp
        \__tenkz_kernel_r_route_edge:nnnN
          { -\l__tenkz_kernel_ro_vx_tl }
          { -\l__tenkz_kernel_ro_vy_tl } {-} \l__tenkz_kernel_ro_y_fp
        \prop_put:Nne \l__tenkz_kernel_ro_box_prop {#1}
          {
            { \fp_eval:n { \l__tenkz_kernel_ro_x_fp  } }
            { \fp_eval:n { \l__tenkz_kernel_ro_xb_fp } }
            { \fp_eval:n { \l__tenkz_kernel_ro_y_fp  } }
            { \fp_eval:n { \l__tenkz_kernel_ro_yb_fp } }
            { \tl_use:N  \l__tenkz_kernel_ro_ex_tl  }
            { \tl_use:N  \l__tenkz_kernel_ro_ey_tl  }
            { \tl_use:N  \l__tenkz_kernel_ro_nx_tl  }
            { \tl_use:N  \l__tenkz_kernel_ro_ny_tl  }
          }
        \prop_put:Nne \l__tenkz_kernel_ro_lane_prop {#1}
          {
            \fp_eval:n
              {
                \str_case:Vn \l__tenkz_kernel_ro_side_tl
                  {
                    {n}   { \l__tenkz_kernel_ro_yb_fp }
                    {s}   { \l__tenkz_kernel_ro_y_fp  }
                    {e}   { \l__tenkz_kernel_ro_xb_fp }
                    {w}   { \l__tenkz_kernel_ro_x_fp  }
                    {all} { \l__tenkz_kernel_ro_yb_fp }
                  }
              }
          }
      }
  }
% one edge of the offset hull, pushed out by the lanes already standing there
\cs_new_protected:Npn \__tenkz_kernel_r_route_edge:nnnN #1#2#3#4
  {
    % one clearance is the hull's own, the next is this route's, and one more
    % for every route already standing over the same selection
    \__tenkz_kernel_hull_reach_linear:NnnnN
      \l__tenkz_kernel_ro_obst_seq {#1}{#2}
      {
        ( \l__tenkz_kernel_ro_lane_tl + 2 )
        * \__tenkz_metric_ratio:n {daylight} * \l__tenkz_kernel_ro_scale_tl
      }
      \l__tenkz_kernel_ro_reach_tl
    \fp_set:Nn #4 { #3 \l__tenkz_kernel_ro_reach_tl }
  }

% `crossing of a and b` is where the two strings actually meet, so the
% address asks the string engine for the point saved before crossing surgery.
% Undeclared pairs still read the live intersection of their routes.
\cs_new_protected:Npn \__tenkz_kernel_r_crossing:nnn #1#2#3
  {
    \__tenkz_string_crossing_point:nnN
      {#2} {#3} \l__tenkz_kernel_r_tl
    \quark_if_no_value:NTF \l__tenkz_kernel_r_tl
      {
        \__tenkz_string_count:nnN
          {#2} {#3} \l__tenkz_kernel_r_cross_int
        \int_compare:nNnTF { \l__tenkz_kernel_r_cross_int } > {0}
          {
            \pgfpointintersectionsolution {1}
            \__tenkz_geom_store:nnn {#1}
              {
                \dim_to_fp:n { \use:c {pgf@x} }
                / \dim_to_fp:n { \tenkz@pitch }
              }
              {
                \dim_to_fp:n { \use:c {pgf@y} }
                / \dim_to_fp:n { \tenkz@pitch }
              }
          }
          { \msg_error:nnnn {tenkz}{kernel-render-nocross} {#2} {#3} }
      }
      {
        \__tenkz_geom_store:nnn {#1}
          {
            \dim_to_fp:n { \tl_item:Nn \l__tenkz_kernel_r_tl {1} }
            / \dim_to_fp:n { \tenkz@pitch }
          }
          {
            \dim_to_fp:n { \tl_item:Nn \l__tenkz_kernel_r_tl {2} }
            / \dim_to_fp:n { \tenkz@pitch }
          }
      }
  }
\int_new:N \l__tenkz_kernel_r_cross_int
\msg_new:nnn {tenkz}{kernel-render-nocross}
  { [TKZ-KERNEL-RENDER-NOCROSS]~an~address~names~the~crossing~of~'#1'~and~
    '#2',~but~their~saved~routes~do~not~meet. }

% A port is a cell of its atom's span, chosen by the face's angle in the
% atom's OWN axes: a face within a right angle of east takes the eastmost
% spanned column, one within a right angle of north the northmost spanned
% row, and along the remaining axis the slot walks the span.  The cell then
% resolves through the frame, so a port on a turned carrier turns with it.
% The wire the port anchors runs under the atom's skin, so the span cell IS
% the port at this stage's altitude.
\tl_new:N \l__tenkz_kernel_r_face_angle_tl
\tl_new:N \l__tenkz_kernel_r_face_side_tl
\cs_new_protected:Npn \__tenkz_kernel_r_face_side:n #1
  {
    \tl_set:Ne \l__tenkz_kernel_r_face_angle_tl
      { \fp_eval:n { #1 - 360 * floor( (#1) / 360 ) } }
    % The four diagonal rays belong counter-clockwise: 45 to north, 135 to
    % west, 225 (-135) to south, and 315 (-45) to east.  This half-open
    % partition avoids asking approximate sine and cosine values for equality.
    \fp_compare:nNnTF { \l__tenkz_kernel_r_face_angle_tl } < {45}
      { \tl_set:Nn \l__tenkz_kernel_r_face_side_tl {e} }
      {
        \fp_compare:nNnTF { \l__tenkz_kernel_r_face_angle_tl } < {135}
          { \tl_set:Nn \l__tenkz_kernel_r_face_side_tl {n} }
          {
            \fp_compare:nNnTF { \l__tenkz_kernel_r_face_angle_tl } < {225}
              { \tl_set:Nn \l__tenkz_kernel_r_face_side_tl {w} }
              {
                \fp_compare:nNnTF
                  { \l__tenkz_kernel_r_face_angle_tl } < {315}
                  { \tl_set:Nn \l__tenkz_kernel_r_face_side_tl {s} }
                  { \tl_set:Nn \l__tenkz_kernel_r_face_side_tl {e} }
              }
          }
      }
  }
\cs_generate_variant:Nn \__tenkz_kernel_r_face_side:n { e }
\cs_new_protected:Npn \__tenkz_kernel_r_port_record:nN #1#2
  {
    \prop_get:NeN \l__tenkz_kernel_node_prop {#1/record-id} #2
    \quark_if_no_value:NT #2
      {
        \prop_get:NeN \l__tenkz_kernel_named_prop
          { \__tenkz_kernel_r_item:nn {#1} {record} }
          #2
      }
  }

% The affine carrier of a label target, when it has one.  A cell and one of
% its basis members share the frame chart at (row, column).  Named records
% answer through the atom they name; a cluster child inherits the chart of
% its named parent, and a port inherits the chart of its host.  Composite and
% curve addresses deliberately answer false: relative, midway, crossing, and
% on-wire nodes are page-space points rather than local-frame carriers.
\tl_new:N \l__tenkz_kernel_carrier_parent_tl
\tl_new:N \l__tenkz_kernel_carrier_record_tl
\cs_new_protected:Npn \__tenkz_kernel_r_record_carrier:nNNN #1#2#3#4
  {
    \__tenkz_kernel_atom_rc:nNNN {#1} #2#3#4
    \bool_if:NF #4
      {
        \__tenkz_model_get:nnN {#1} {node}
          \l__tenkz_kernel_carrier_record_tl
        \quark_if_no_value:NF \l__tenkz_kernel_carrier_record_tl
          {
            \exp_args:NV \__tenkz_kernel_r_node_carrier:nNNN
              \l__tenkz_kernel_carrier_record_tl #2#3#4
          }
      }
    \bool_if:NF #4
      {
        \__tenkz_model_get:nnN {#1} {cluster-of}
          \l__tenkz_kernel_carrier_parent_tl
        \quark_if_no_value:NF \l__tenkz_kernel_carrier_parent_tl
          {
            \prop_get:NVN \l__tenkz_kernel_named_prop
              \l__tenkz_kernel_carrier_parent_tl
              \l__tenkz_kernel_carrier_record_tl
            \quark_if_no_value:NF \l__tenkz_kernel_carrier_record_tl
              {
                \exp_args:NV \__tenkz_kernel_r_record_carrier:nNNN
                  \l__tenkz_kernel_carrier_record_tl #2#3#4
              }
          }
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_r_node_carrier:nNNN #1#2#3#4
  {
    \bool_set_false:N #4
    \str_case:en
      { \__tenkz_kernel_r_item:nn {#1} {kind} }
      {
        {cell}
          {
            \tl_set:Ne #2 { \__tenkz_kernel_r_item:nn {#1} {row} }
            \tl_set:Ne #3 { \__tenkz_kernel_r_item:nn {#1} {col} }
            \bool_set_true:N #4
          }
        {member}
          {
            \tl_set:Ne #2 { \__tenkz_kernel_r_item:nn {#1} {row} }
            \tl_set:Ne #3 { \__tenkz_kernel_r_item:nn {#1} {col} }
            \bool_set_true:N #4
          }
        {record}
          {
            \prop_get:NeN \l__tenkz_kernel_named_prop
              { \__tenkz_kernel_r_item:nn {#1} {name} }
              \l__tenkz_kernel_carrier_record_tl
            \quark_if_no_value:NF \l__tenkz_kernel_carrier_record_tl
              {
                \exp_args:NV \__tenkz_kernel_r_record_carrier:nNNN
                  \l__tenkz_kernel_carrier_record_tl #2#3#4
              }
          }
        {port}
          {
            \__tenkz_kernel_r_port_record:nN {#1}
              \l__tenkz_kernel_carrier_record_tl
            \quark_if_no_value:NF \l__tenkz_kernel_carrier_record_tl
              {
                \exp_args:NV \__tenkz_kernel_r_record_carrier:nNNN
                  \l__tenkz_kernel_carrier_record_tl #2#3#4
              }
          }
      }
  }

% pgf border anchors answer on the measurement node's sharp rectangle, so a
% numeric-bearing anchor on a measured pill overshoots the caps.  Re-meet the
% rounded silhouette at the same bearing: the measurement node's own anchors
% supply the centre and half-extents, the live named pill-cap radius plus the
% half-stroke those anchors already include supplies the caps, and the shared
% roundrect ray query supplies the reach.  Named anchors (north, east, ...)
% stand on the flat sides, which the caps never touch, and pass unchanged.
% The two dim registers below are the readers' anchor outputs, adjusted in
% place after every measurement-node read.
\fp_new:N \l__tenkz_kernel_pill_hx_fp
\fp_new:N \l__tenkz_kernel_pill_hy_fp
\fp_new:N \l__tenkz_kernel_pill_cx_fp
\fp_new:N \l__tenkz_kernel_pill_cy_fp
\fp_new:N \l__tenkz_kernel_pill_t_fp
\cs_new_protected:Npn \__tenkz_kernel_r_pill_trim_anchor:nn #1#2
  {
    \__tenkz_kernel_hull_resolve_skin:n {#1}
    \str_if_eq:VnT \l__tenkz_kernel_hull_skin_tl {roundrect}
      {
        \regex_match:nnT { \A \-? [0-9]+ ( \. [0-9]* )? \Z } {#2}
          { \__tenkz_kernel_r_pill_trim_anchor_aux:nn {#1} {#2} }
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_r_pill_trim_anchor_aux:nn #1#2
  {
    \tl_set:Ne \l__tenkz_kernel_hull_measure_name_tl
      { tenkz_hull_\int_use:N \g__tenkz_kernel_picture_int _#1 }
    \pgfextractx \l__tenkz_kernel_hull_west_dim
      {
        \pgfpointanchor
          {\tl_use:N \l__tenkz_kernel_hull_measure_name_tl}{west}
      }
    \pgfextractx \l__tenkz_kernel_hull_east_dim
      {
        \pgfpointanchor
          {\tl_use:N \l__tenkz_kernel_hull_measure_name_tl}{east}
      }
    \pgfextracty \l__tenkz_kernel_hull_south_dim
      {
        \pgfpointanchor
          {\tl_use:N \l__tenkz_kernel_hull_measure_name_tl}{south}
      }
    \pgfextracty \l__tenkz_kernel_hull_north_dim
      {
        \pgfpointanchor
          {\tl_use:N \l__tenkz_kernel_hull_measure_name_tl}{north}
      }
    \fp_set:Nn \l__tenkz_kernel_pill_hx_fp
      {
        ( \dim_to_fp:n { \l__tenkz_kernel_hull_east_dim }
          - \dim_to_fp:n { \l__tenkz_kernel_hull_west_dim } ) / 2
      }
    \fp_set:Nn \l__tenkz_kernel_pill_hy_fp
      {
        ( \dim_to_fp:n { \l__tenkz_kernel_hull_north_dim }
          - \dim_to_fp:n { \l__tenkz_kernel_hull_south_dim } ) / 2
      }
    \fp_set:Nn \l__tenkz_kernel_pill_cx_fp
      {
        ( \dim_to_fp:n { \l__tenkz_kernel_hull_east_dim }
          + \dim_to_fp:n { \l__tenkz_kernel_hull_west_dim } ) / 2
      }
    \fp_set:Nn \l__tenkz_kernel_pill_cy_fp
      {
        ( \dim_to_fp:n { \l__tenkz_kernel_hull_north_dim }
          + \dim_to_fp:n { \l__tenkz_kernel_hull_south_dim } ) / 2
      }
    \__tenkz_geom_roundrect_ray:nnnnN
      { \l__tenkz_kernel_pill_hx_fp }
      { \l__tenkz_kernel_pill_hy_fp }
      {
        \dim_to_fp:n { \__tenkz_dim:n {pillcap} }
        + \dim_to_fp:n { \__tenkz_dim:n {wirewidth} } / 2
      }
      {#2}
      \l__tenkz_kernel_pill_t_fp
    \dim_set:Nn \l__tenkz_kernel_r_north_x_dim
      {
        \fp_to_dim:n
          {
            \l__tenkz_kernel_pill_cx_fp
            + \l__tenkz_kernel_pill_t_fp * cosd(#2)
          }
      }
    \dim_set:Nn \l__tenkz_kernel_r_north_y_dim
      {
        \fp_to_dim:n
          {
            \l__tenkz_kernel_pill_cy_fp
            + \l__tenkz_kernel_pill_t_fp * sind(#2)
          }
      }
  }

% Read one measurement-node anchor in the glyph's own east/north axes.  A live
% hull measurement is reused when present; an unscheduled ordinary dot instead
% gets a private face node so the query cannot replace its label-aware fallback
% support.  Both paths use the final skin style rather than print constants.
\fp_new:N \l__tenkz_kernel_r_face_u_fp
\fp_new:N \l__tenkz_kernel_r_face_v_fp
\tl_new:N \l__tenkz_kernel_r_face_du_tl
\tl_new:N \l__tenkz_kernel_r_face_dv_tl
\fp_new:N \l__tenkz_kernel_r_face_cx_fp
\fp_new:N \l__tenkz_kernel_r_face_cy_fp
% `none` may own synthetic support for an authored port or a pairing, but
% without a declared pairing it has no glyph ink face for this query.
\cs_new_protected:Npn \__tenkz_kernel_r_glyph_face_validate:nN #1#2
  {
    \bool_set_false:N #2
    \__tenkz_model_get:nnN {#1} {cluster} \l__tenkz_kernel_hull_a_tl
    \quark_if_no_value:NTF \l__tenkz_kernel_hull_a_tl
      {
        \bool_set_true:N #2
        \__tenkz_kernel_hull_resolve_skin:n {#1}
        \str_if_eq:VnT \l__tenkz_kernel_hull_skin_tl {none}
          {
            \prop_if_in:NnF \l__tenkz_kernel_skin_pairing_host_prop {#1}
              {
                \bool_set_false:N #2
                \msg_error:nnn {tenkz}{kernel-glyph-face-none}{#1}
              }
          }
      }
      { \msg_error:nnn {tenkz}{kernel-glyph-face-cluster}{#1} }
  }
\cs_new_protected:Npn \__tenkz_kernel_r_glyph_anchor_local:nnNN #1#2#3#4
  {
    \__tenkz_kernel_r_glyph_face_validate:nN {#1}
      \l__tenkz_kernel_hull_measure_valid_bool
    \bool_if:NT \l__tenkz_kernel_hull_measure_valid_bool
      {
        \__tenkz_kernel_glyph_face_ensure_measured:n {#1}
      }
    \bool_if:NTF \l__tenkz_kernel_hull_measure_valid_bool
      { \__tenkz_kernel_r_glyph_anchor_local_aux:nnNN {#1}{#2}#3#4 }
      {
        \fp_zero:N #3
        \fp_zero:N #4
      }
  }
% Read compass-angle anchor #2 from the record's measurement node.  The
% outputs are glyph-local east/north coordinates normalized by picture pitch.
\cs_new_protected:Npn \__tenkz_kernel_r_glyph_anchor_local_aux:nnNN #1#2#3#4
  {
    \tl_set:Ne \l__tenkz_kernel_hull_measure_name_tl
      { tenkz_hull_\int_use:N \g__tenkz_kernel_picture_int _#1 }
    \pgfextractx \l__tenkz_kernel_r_north_x_dim
      {
        \pgfpointanchor
          {\tl_use:N \l__tenkz_kernel_hull_measure_name_tl}{#2}
      }
    \pgfextracty \l__tenkz_kernel_r_north_y_dim
      {
        \pgfpointanchor
          {\tl_use:N \l__tenkz_kernel_hull_measure_name_tl}{#2}
      }
    \__tenkz_kernel_r_pill_trim_anchor:nn {#1} {#2}
    \fp_set:Nn #3
      {
        \dim_to_fp:n { \l__tenkz_kernel_r_north_x_dim }
        / \dim_to_fp:n { \use:c {tenkz@pitch} }
      }
    \fp_set:Nn #4
      {
        \dim_to_fp:n { \l__tenkz_kernel_r_north_y_dim }
        / \dim_to_fp:n { \use:c {tenkz@pitch} }
      }
  }

% Carry a measured glyph anchor through its complete record carrier.  Live
% ports, pairings, and planned physical legs share this one face boundary.
\cs_new_protected:Npn \__tenkz_kernel_r_glyph_anchor_xy:nnNN #1#2#3#4
  {
    \__tenkz_kernel_r_glyph_anchor_local:nnNN {#1}{#2}
      \l__tenkz_kernel_r_face_u_fp \l__tenkz_kernel_r_face_v_fp
    \bool_if:NTF \l__tenkz_kernel_hull_measure_valid_bool
      {
        \__tenkz_kernel_r_carrier_basis:n {#1}
        \__tenkz_geom_basis_apply:nnnnnnNN
          { \l__tenkz_kernel_r_basis_ex_tl }
          { \l__tenkz_kernel_r_basis_ey_tl }
          { \l__tenkz_kernel_r_basis_nx_tl }
          { \l__tenkz_kernel_r_basis_ny_tl }
          { \l__tenkz_kernel_r_face_u_fp }
          { \l__tenkz_kernel_r_face_v_fp }
          \l__tenkz_kernel_r_face_du_tl \l__tenkz_kernel_r_face_dv_tl
        \__tenkz_geom_xy:nNN {#1}
          \l__tenkz_kernel_r_face_cx_fp \l__tenkz_kernel_r_face_cy_fp
        \fp_set:Nn #3
          { \l__tenkz_kernel_r_face_cx_fp + \l__tenkz_kernel_r_face_du_tl }
        \fp_set:Nn #4
          { \l__tenkz_kernel_r_face_cy_fp + \l__tenkz_kernel_r_face_dv_tl }
      }
      {
        \fp_zero:N #3
        \fp_zero:N #4
      }
  }

% A physical carrier read that tolerates a barren `none` host.  A void hole
% keeps its physical indices, so the closure ink that consumes them is
% legitimate even though the hole has no glyph face to probe.  Its support
% degrades to the host's site point -- the same centre-origin fallback the
% policy-leg renderer established -- while every other host keeps the exact
% measured glyph face.  A direct authored face query still raises the
% face-none diagnostic through \__tenkz_kernel_r_glyph_face_validate:nN.
\cs_new_protected:Npn \__tenkz_kernel_r_glyph_anchor_or_site_xy:nnNN #1#2#3#4
  {
    \bool_set_true:N \l__tenkz_kernel_hull_measure_valid_bool
    \__tenkz_kernel_hull_resolve_skin:n {#1}
    \str_if_eq:VnT \l__tenkz_kernel_hull_skin_tl {none}
      {
        \prop_if_in:NnF \l__tenkz_kernel_skin_pairing_host_prop {#1}
          { \bool_set_false:N \l__tenkz_kernel_hull_measure_valid_bool }
      }
    \bool_if:NT \l__tenkz_kernel_hull_measure_valid_bool
      { \__tenkz_kernel_r_glyph_anchor_xy:nnNN {#1}{#2}#3#4 }
    \bool_if:NF \l__tenkz_kernel_hull_measure_valid_bool
      { \__tenkz_kernel_r_xy:nNN {#1} #3#4 }
  }

% ---------- physical-leg lane plan -------------------------------------------
% The planner is data-only: the physical-leg renderer collects every eligible
% request, resolves once, then consumes the resulting S--B--T paths.  Corridors
% are discrete topology identities supplied by the caller; floating coordinates
% are never clustered to invent one.
\seq_new:N  \l__tenkz_kernel_lp_sort_seq
\prop_new:N \l__tenkz_kernel_lp_corridor_prop
\prop_new:N \l__tenkz_kernel_lp_request_prop
\prop_new:N \l__tenkz_kernel_lp_result_prop
\prop_new:N \l__tenkz_kernel_lp_occupied_prop
\tl_new:N   \l__tenkz_kernel_lp_cid_tl
\tl_new:N   \l__tenkz_kernel_lp_request_cid_tl
\tl_new:N   \l__tenkz_kernel_lp_sid_tl
\tl_new:N   \l__tenkz_kernel_lp_tuple_tl
\tl_new:N   \l__tenkz_kernel_lp_old_tl
\tl_new:N   \l__tenkz_kernel_lp_face_tl
\fp_new:N   \l__tenkz_kernel_lp_dx_fp
\fp_new:N   \l__tenkz_kernel_lp_dy_fp
\fp_new:N   \l__tenkz_kernel_lp_qx_fp
\fp_new:N   \l__tenkz_kernel_lp_qy_fp
\fp_new:N   \l__tenkz_kernel_lp_gx_fp
\fp_new:N   \l__tenkz_kernel_lp_gy_fp
\fp_new:N   \l__tenkz_kernel_lp_norm_fp
\fp_new:N   \l__tenkz_kernel_lp_sx_fp
\fp_new:N   \l__tenkz_kernel_lp_sy_fp
\fp_new:N   \l__tenkz_kernel_lp_reach_fp
\fp_new:N   \l__tenkz_kernel_lp_start_fp
\fp_new:N   \l__tenkz_kernel_lp_end_fp
\fp_new:N   \l__tenkz_kernel_lp_bx_fp
\fp_new:N   \l__tenkz_kernel_lp_by_fp
\fp_new:N   \l__tenkz_kernel_lp_safe_fp
\fp_new:N   \l__tenkz_kernel_lp_tx_fp
\fp_new:N   \l__tenkz_kernel_lp_ty_fp
\int_new:N  \l__tenkz_kernel_lp_lane_int
\bool_new:N \l__tenkz_kernel_lp_same_bool
\bool_new:N \l__tenkz_kernel_lp_conflict_bool

\cs_new_protected:Npn \__tenkz_kernel_leg_plan_reset:
  {
    \seq_clear:N \l__tenkz_kernel_lp_sort_seq
    \prop_clear:N \l__tenkz_kernel_lp_corridor_prop
    \prop_clear:N \l__tenkz_kernel_lp_request_prop
    \prop_clear:N \l__tenkz_kernel_lp_result_prop
    \prop_clear:N \l__tenkz_kernel_lp_occupied_prop
  }

% Declare one topology-owned corridor.  The outward vector becomes the unit
% direction d.  Its page-perpendicular q is oriented toward carrier east, so
% one lane is exactly one page-space daylight in a stable handedness.  A valid
% carrier basis makes east nonparallel to the outward direction.
\cs_new_protected:Npn \__tenkz_kernel_leg_plan_corridor:nnnnn #1#2#3#4#5
  {
    \tl_set:Ne \l__tenkz_kernel_lp_cid_tl {#1}
    \fp_set:Nn \l__tenkz_kernel_lp_dx_fp {#2}
    \fp_set:Nn \l__tenkz_kernel_lp_dy_fp {#3}
    \fp_set:Nn \l__tenkz_kernel_lp_norm_fp
      {
        sqrt(
          \l__tenkz_kernel_lp_dx_fp ^ 2
          + \l__tenkz_kernel_lp_dy_fp ^ 2 )
      }
    \fp_compare:nNnTF {\l__tenkz_kernel_lp_norm_fp} = {0}
      {
        \msg_error:nne {tenkz}{kernel-leg-plan-vector}
          {\tl_use:N \l__tenkz_kernel_lp_cid_tl}
      }
      {
        \fp_set:Nn \l__tenkz_kernel_lp_dx_fp
          { \l__tenkz_kernel_lp_dx_fp / \l__tenkz_kernel_lp_norm_fp }
        \fp_set:Nn \l__tenkz_kernel_lp_dy_fp
          { \l__tenkz_kernel_lp_dy_fp / \l__tenkz_kernel_lp_norm_fp }
        \fp_set:Nn \l__tenkz_kernel_lp_qx_fp
          { -\l__tenkz_kernel_lp_dy_fp }
        \fp_set_eq:NN \l__tenkz_kernel_lp_qy_fp
          \l__tenkz_kernel_lp_dx_fp
        \fp_compare:nNnT
          {
            \l__tenkz_kernel_lp_qx_fp * (#4)
            + \l__tenkz_kernel_lp_qy_fp * (#5)
          }
          < {0}
          {
            \fp_set:Nn \l__tenkz_kernel_lp_qx_fp
              { -\l__tenkz_kernel_lp_qx_fp }
            \fp_set:Nn \l__tenkz_kernel_lp_qy_fp
              { -\l__tenkz_kernel_lp_qy_fp }
          }
        % The carrier-east line is the affine face tangent.  Its oriented
        % page covector detects whether a lane shift would enter the host.
        \fp_set:Nn \l__tenkz_kernel_lp_gx_fp {-(#5)}
        \fp_set:Nn \l__tenkz_kernel_lp_gy_fp {#4}
        \fp_compare:nNnT
          {
            \l__tenkz_kernel_lp_gx_fp * \l__tenkz_kernel_lp_dx_fp
            + \l__tenkz_kernel_lp_gy_fp * \l__tenkz_kernel_lp_dy_fp
          }
          < {0}
          {
            \fp_set:Nn \l__tenkz_kernel_lp_gx_fp
              {-\l__tenkz_kernel_lp_gx_fp}
            \fp_set:Nn \l__tenkz_kernel_lp_gy_fp
              {-\l__tenkz_kernel_lp_gy_fp}
          }
        \tl_set:Ne \l__tenkz_kernel_lp_tuple_tl
          {
            {\fp_use:N \l__tenkz_kernel_lp_dx_fp}
            {\fp_use:N \l__tenkz_kernel_lp_dy_fp}
            {\fp_use:N \l__tenkz_kernel_lp_qx_fp}
            {\fp_use:N \l__tenkz_kernel_lp_qy_fp}
            {\fp_use:N \l__tenkz_kernel_lp_gx_fp}
            {\fp_use:N \l__tenkz_kernel_lp_gy_fp}
          }
        \prop_get:NVN \l__tenkz_kernel_lp_corridor_prop
          \l__tenkz_kernel_lp_cid_tl \l__tenkz_kernel_lp_old_tl
        \quark_if_no_value:NTF \l__tenkz_kernel_lp_old_tl
          {
            \prop_put:NVV \l__tenkz_kernel_lp_corridor_prop
              \l__tenkz_kernel_lp_cid_tl \l__tenkz_kernel_lp_tuple_tl
          }
          {
            \tl_if_eq:NNTF
              \l__tenkz_kernel_lp_old_tl \l__tenkz_kernel_lp_tuple_tl
              { }
              {
                \msg_error:nne {tenkz}{kernel-leg-plan-basis}
                  {\tl_use:N \l__tenkz_kernel_lp_cid_tl}
              }
          }
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_leg_plan_request_set:nnnn #1#2#3#4
  {
    \tl_set:Nn \l__tenkz_kernel_lp_request_cid_tl {#1}
    \fp_set:Nn \l__tenkz_kernel_lp_sx_fp {#2}
    \fp_set:Nn \l__tenkz_kernel_lp_sy_fp {#3}
    \fp_set:Nn \l__tenkz_kernel_lp_reach_fp {#4}
  }

\cs_new_protected:Npn \__tenkz_kernel_leg_plan_enrol_new:
  {
    \prop_put:NVV \l__tenkz_kernel_lp_request_prop
      \l__tenkz_kernel_lp_sid_tl \l__tenkz_kernel_lp_tuple_tl
    \prop_clear:N \l__tenkz_kernel_lp_result_prop
  }

% Enrol one face-relative outward interval.  Repeated observations of the
% same semantic leg share one exact face and retain only their greatest reach.
\cs_new_protected:Npn \__tenkz_kernel_leg_plan_enrol:nnnnn #1#2#3#4#5
  {
    \tl_set:Ne \l__tenkz_kernel_lp_sid_tl {#1}
    \tl_set:Ne \l__tenkz_kernel_lp_cid_tl {#2}
    \fp_set:Nn \l__tenkz_kernel_lp_sx_fp {#3}
    \fp_set:Nn \l__tenkz_kernel_lp_sy_fp {#4}
    \fp_set:Nn \l__tenkz_kernel_lp_reach_fp {#5}
    % Freeze caller expressions before unpacking an earlier request into the
    % planner's scratch registers.
    \tl_set:Ne \l__tenkz_kernel_lp_tuple_tl
      {
        {\tl_use:N \l__tenkz_kernel_lp_cid_tl}
        {\fp_use:N \l__tenkz_kernel_lp_sx_fp}
        {\fp_use:N \l__tenkz_kernel_lp_sy_fp}
        {\fp_use:N \l__tenkz_kernel_lp_reach_fp}
      }
    \fp_compare:nNnTF {\l__tenkz_kernel_lp_reach_fp} < {0}
      {
        \msg_error:nnee {tenkz}{kernel-leg-plan-reach}
          {\tl_use:N \l__tenkz_kernel_lp_sid_tl}
          {\fp_use:N \l__tenkz_kernel_lp_reach_fp}
      }
      {
        \prop_if_in:NVTF \l__tenkz_kernel_lp_corridor_prop
          \l__tenkz_kernel_lp_cid_tl
          {
            \prop_get:NVN \l__tenkz_kernel_lp_request_prop
              \l__tenkz_kernel_lp_sid_tl \l__tenkz_kernel_lp_old_tl
            \quark_if_no_value:NTF \l__tenkz_kernel_lp_old_tl
              { \__tenkz_kernel_leg_plan_enrol_new: }
              {
                \exp_last_unbraced:NV
                  \__tenkz_kernel_leg_plan_request_set:nnnn
                  \l__tenkz_kernel_lp_old_tl
                \bool_set_true:N \l__tenkz_kernel_lp_same_bool
                \str_if_eq:eeF
                  {\tl_use:N \l__tenkz_kernel_lp_request_cid_tl}
                  {\tl_item:Nn \l__tenkz_kernel_lp_tuple_tl {1}}
                  { \bool_set_false:N \l__tenkz_kernel_lp_same_bool }
                \fp_compare:nNnF
                  {\l__tenkz_kernel_lp_sx_fp}
                  = {\tl_item:Nn \l__tenkz_kernel_lp_tuple_tl {2}}
                  { \bool_set_false:N \l__tenkz_kernel_lp_same_bool }
                \fp_compare:nNnF
                  {\l__tenkz_kernel_lp_sy_fp}
                  = {\tl_item:Nn \l__tenkz_kernel_lp_tuple_tl {3}}
                  { \bool_set_false:N \l__tenkz_kernel_lp_same_bool }
                \bool_if:NTF \l__tenkz_kernel_lp_same_bool
                  {
                    \fp_compare:nNnT
                      {\l__tenkz_kernel_lp_reach_fp}
                      < {\tl_item:Nn \l__tenkz_kernel_lp_tuple_tl {4}}
                      {
                        \tl_set:Ne \l__tenkz_kernel_lp_old_tl
                          {
                            {\tl_use:N \l__tenkz_kernel_lp_request_cid_tl}
                            {\fp_use:N \l__tenkz_kernel_lp_sx_fp}
                            {\fp_use:N \l__tenkz_kernel_lp_sy_fp}
                            {\tl_item:Nn \l__tenkz_kernel_lp_tuple_tl {4}}
                          }
                        \prop_put:NVV \l__tenkz_kernel_lp_request_prop
                          \l__tenkz_kernel_lp_sid_tl
                          \l__tenkz_kernel_lp_old_tl
                        \prop_clear:N \l__tenkz_kernel_lp_result_prop
                      }
                  }
                  {
                    \tl_set:Ne \l__tenkz_kernel_lp_old_tl
                      {
                        {\tl_use:N \l__tenkz_kernel_lp_request_cid_tl}
                        {\fp_use:N \l__tenkz_kernel_lp_sx_fp}
                        {\fp_use:N \l__tenkz_kernel_lp_sy_fp}
                      }
                    \tl_set:Ne \l__tenkz_kernel_lp_face_tl
                      {
                        {\tl_item:Nn \l__tenkz_kernel_lp_tuple_tl {1}}
                        {\tl_item:Nn \l__tenkz_kernel_lp_tuple_tl {2}}
                        {\tl_item:Nn \l__tenkz_kernel_lp_tuple_tl {3}}
                      }
                    \msg_error:nneee {tenkz}{kernel-leg-plan-face}
                      {\tl_use:N \l__tenkz_kernel_lp_sid_tl}
                      {\tl_use:N \l__tenkz_kernel_lp_old_tl}
                      {\tl_use:N \l__tenkz_kernel_lp_face_tl}
                  }
              }
          }
          {
            \msg_error:nnee {tenkz}{kernel-leg-plan-corridor}
              {\tl_use:N \l__tenkz_kernel_lp_sid_tl}
              {\tl_use:N \l__tenkz_kernel_lp_cid_tl}
          }
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_leg_plan_basis_set:nnnnnn
    #1#2#3#4#5#6
  {
    \fp_set:Nn \l__tenkz_kernel_lp_dx_fp {#1}
    \fp_set:Nn \l__tenkz_kernel_lp_dy_fp {#2}
    \fp_set:Nn \l__tenkz_kernel_lp_qx_fp {#3}
    \fp_set:Nn \l__tenkz_kernel_lp_qy_fp {#4}
    \fp_set:Nn \l__tenkz_kernel_lp_gx_fp {#5}
    \fp_set:Nn \l__tenkz_kernel_lp_gy_fp {#6}
  }

\cs_new_protected:Npn \__tenkz_kernel_leg_plan_sort_item:nnn #1#2#3
  {
    \tl_set:Nn \l__tenkz_kernel_lp_sid_tl {#1}
    \fp_set:Nn \l__tenkz_kernel_lp_start_fp {#2}
    \fp_set:Nn \l__tenkz_kernel_lp_end_fp {#3}
  }

\cs_new_protected:Npn \__tenkz_kernel_leg_plan_conflict:
  {
    \bool_set_false:N \l__tenkz_kernel_lp_conflict_bool
    \prop_get:NeN \l__tenkz_kernel_lp_occupied_prop
      {\int_use:N \l__tenkz_kernel_lp_lane_int}
      \l__tenkz_kernel_lp_old_tl
    \quark_if_no_value:NF \l__tenkz_kernel_lp_old_tl
      {
        % Descending starts make the most recently placed start the only
        % boundary to check.  Equality still conflicts: intervals are closed.
        \fp_compare:nNnF
          {\l__tenkz_kernel_lp_end_fp}
          < {\l__tenkz_kernel_lp_old_tl}
          { \bool_set_true:N \l__tenkz_kernel_lp_conflict_bool }
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_leg_plan_resolve_one:n #1
  {
    \__tenkz_kernel_leg_plan_sort_item:nnn #1
    \prop_get:NVN \l__tenkz_kernel_lp_request_prop
      \l__tenkz_kernel_lp_sid_tl \l__tenkz_kernel_lp_old_tl
    \exp_last_unbraced:NV
      \__tenkz_kernel_leg_plan_request_set:nnnn
      \l__tenkz_kernel_lp_old_tl
    \int_zero:N \l__tenkz_kernel_lp_lane_int
    \bool_set_true:N \l__tenkz_kernel_lp_conflict_bool
    \bool_while_do:Nn \l__tenkz_kernel_lp_conflict_bool
      {
        \__tenkz_kernel_leg_plan_conflict:
        \bool_if:NT \l__tenkz_kernel_lp_conflict_bool
          { \int_incr:N \l__tenkz_kernel_lp_lane_int }
      }
    \prop_put:Nee \l__tenkz_kernel_lp_occupied_prop
      {\int_use:N \l__tenkz_kernel_lp_lane_int}
      {\fp_use:N \l__tenkz_kernel_lp_start_fp}
    % A nonzero lane first moves along an outward-safe diagonal.  The affine
    % face covector chooses the least outward correction whose whole S--B
    % segment stays on or beyond the supporting face.
    \fp_set:Nn \l__tenkz_kernel_lp_safe_fp
      {
        max(
          0,
          -\l__tenkz_kernel_lp_lane_int
            * \__tenkz_metric_ratio:n {daylight}
            * ( \l__tenkz_kernel_lp_gx_fp
                  * \l__tenkz_kernel_lp_qx_fp
                + \l__tenkz_kernel_lp_gy_fp
                  * \l__tenkz_kernel_lp_qy_fp )
            / ( \l__tenkz_kernel_lp_gx_fp
                  * \l__tenkz_kernel_lp_dx_fp
                + \l__tenkz_kernel_lp_gy_fp
                  * \l__tenkz_kernel_lp_dy_fp ) )
      }
    \fp_set:Nn \l__tenkz_kernel_lp_bx_fp
      {
        \l__tenkz_kernel_lp_sx_fp
        + \l__tenkz_kernel_lp_safe_fp * \l__tenkz_kernel_lp_dx_fp
        + \l__tenkz_kernel_lp_lane_int
          * \__tenkz_metric_ratio:n {daylight}
          * \l__tenkz_kernel_lp_qx_fp
      }
    \fp_set:Nn \l__tenkz_kernel_lp_by_fp
      {
        \l__tenkz_kernel_lp_sy_fp
        + \l__tenkz_kernel_lp_safe_fp * \l__tenkz_kernel_lp_dy_fp
        + \l__tenkz_kernel_lp_lane_int
          * \__tenkz_metric_ratio:n {daylight}
          * \l__tenkz_kernel_lp_qy_fp
      }
    \fp_set:Nn \l__tenkz_kernel_lp_tx_fp
      { \l__tenkz_kernel_lp_bx_fp + \l__tenkz_kernel_lp_reach_fp
          * \l__tenkz_kernel_lp_dx_fp }
    \fp_set:Nn \l__tenkz_kernel_lp_ty_fp
      { \l__tenkz_kernel_lp_by_fp + \l__tenkz_kernel_lp_reach_fp
          * \l__tenkz_kernel_lp_dy_fp }
    \tl_set:Ne \l__tenkz_kernel_lp_tuple_tl
      {
        {\int_use:N \l__tenkz_kernel_lp_lane_int}
        {\fp_use:N \l__tenkz_kernel_lp_reach_fp}
        {\fp_use:N \l__tenkz_kernel_lp_sx_fp}
        {\fp_use:N \l__tenkz_kernel_lp_sy_fp}
        {\fp_use:N \l__tenkz_kernel_lp_bx_fp}
        {\fp_use:N \l__tenkz_kernel_lp_by_fp}
        {\fp_use:N \l__tenkz_kernel_lp_tx_fp}
        {\fp_use:N \l__tenkz_kernel_lp_ty_fp}
      }
    \prop_put:NVV \l__tenkz_kernel_lp_result_prop
      \l__tenkz_kernel_lp_sid_tl \l__tenkz_kernel_lp_tuple_tl
  }

\cs_new_protected:Npn \__tenkz_kernel_leg_plan_collect_request:nn #1#2
  {
    \__tenkz_kernel_leg_plan_request_set:nnnn #2
    \str_if_eq:eeT
      {\tl_use:N \l__tenkz_kernel_lp_request_cid_tl}
      {\tl_use:N \l__tenkz_kernel_lp_cid_tl}
      {
        \fp_set:Nn \l__tenkz_kernel_lp_start_fp
          {
            \l__tenkz_kernel_lp_sx_fp * \l__tenkz_kernel_lp_dx_fp
            + \l__tenkz_kernel_lp_sy_fp * \l__tenkz_kernel_lp_dy_fp
          }
        \fp_set:Nn \l__tenkz_kernel_lp_end_fp
          { \l__tenkz_kernel_lp_start_fp + \l__tenkz_kernel_lp_reach_fp }
        \seq_put_right:Ne \l__tenkz_kernel_lp_sort_seq
          {
            {#1}
            {\fp_use:N \l__tenkz_kernel_lp_start_fp}
            {\fp_use:N \l__tenkz_kernel_lp_end_fp}
          }
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_leg_plan_resolve_corridor:nn #1#2
  {
    \tl_set:Nn \l__tenkz_kernel_lp_cid_tl {#1}
    \__tenkz_kernel_leg_plan_basis_set:nnnnnn #2
    \seq_clear:N \l__tenkz_kernel_lp_sort_seq
    \prop_clear:N \l__tenkz_kernel_lp_occupied_prop
    \prop_map_function:NN \l__tenkz_kernel_lp_request_prop
      \__tenkz_kernel_leg_plan_collect_request:nn
    \seq_sort:Nn \l__tenkz_kernel_lp_sort_seq
      {
        \fp_compare:nNnTF
          {\tl_item:nn {##1}{2}} < {\tl_item:nn {##2}{2}}
          { \sort_return_swapped: }
          {
            \fp_compare:nNnTF
              {\tl_item:nn {##1}{2}} > {\tl_item:nn {##2}{2}}
              { \sort_return_same: }
              {
                \fp_compare:nNnTF
                  {\tl_item:nn {##1}{3}} < {\tl_item:nn {##2}{3}}
                  { \sort_return_swapped: }
                  {
                    \fp_compare:nNnTF
                      {\tl_item:nn {##1}{3}} > {\tl_item:nn {##2}{3}}
                      { \sort_return_same: }
                      {
                        \str_compare:eNeTF
                          {\tl_item:nn {##1}{1}}
                          < {\tl_item:nn {##2}{1}}
                          { \sort_return_swapped: }
                          { \sort_return_same: }
                      }
                  }
              }
          }
      }
    \seq_map_function:NN \l__tenkz_kernel_lp_sort_seq
      \__tenkz_kernel_leg_plan_resolve_one:n
  }

\cs_new_protected:Npn \__tenkz_kernel_leg_plan_resolve:
  {
    \prop_clear:N \l__tenkz_kernel_lp_result_prop
    \prop_map_function:NN \l__tenkz_kernel_lp_corridor_prop
      \__tenkz_kernel_leg_plan_resolve_corridor:nn
  }

% Return {lane}{reach}{start-x}{start-y}{bend-x}{bend-y}{tip-x}{tip-y}.
% An unknown or not-yet-resolved semantic id deliberately returns q_no_value.
\cs_new_protected:Npn \__tenkz_kernel_leg_plan_get:nN #1#2
  { \prop_get:NeN \l__tenkz_kernel_lp_result_prop {#1} #2 }

% Resolve a live glyph anchor in the host's local axes and carry it to the
% page.  The resolved port node supplies the span-slot centre, while the host
% glyph supplies the face-normal silhouette displacement.  Non-cell pairing
% ink and ordinary wires share this face boundary.
\cs_new_protected:Npn \__tenkz_kernel_r_hull_port_xy:nnNN #1#2#3#4
  {
    \__tenkz_kernel_hull_ensure_measured:nN {#2}
      \l__tenkz_kernel_hull_measure_valid_bool
    \bool_if:NTF \l__tenkz_kernel_hull_measure_valid_bool
      { \__tenkz_kernel_r_hull_port_xy_aux:nnNN {#1}{#2}#3#4 }
      {
        \fp_zero:N #3
        \fp_zero:N #4
      }
  }
% The aux works in the shared endpoint registers, so it saves them on entry
% and hands back only its two outputs: a wire whose other end was already
% resolved keeps that end's coordinates.
\fp_new:N \l__tenkz_kernel_r_hp_save_x_fp
\fp_new:N \l__tenkz_kernel_r_hp_save_y_fp
\fp_new:N \l__tenkz_kernel_r_hp_save_xb_fp
\fp_new:N \l__tenkz_kernel_r_hp_save_yb_fp
\fp_new:N \l__tenkz_kernel_r_hp_out_x_fp
\fp_new:N \l__tenkz_kernel_r_hp_out_y_fp
\cs_new_protected:Npn \__tenkz_kernel_r_hull_port_xy_aux:nnNN #1#2#3#4
  {
    \fp_set_eq:NN \l__tenkz_kernel_r_hp_save_x_fp \l__tenkz_kernel_r_x_fp
    \fp_set_eq:NN \l__tenkz_kernel_r_hp_save_y_fp \l__tenkz_kernel_r_y_fp
    \fp_set_eq:NN \l__tenkz_kernel_r_hp_save_xb_fp \l__tenkz_kernel_r_xb_fp
    \fp_set_eq:NN \l__tenkz_kernel_r_hp_save_yb_fp \l__tenkz_kernel_r_yb_fp
    \__tenkz_kernel_r_node:n {#1}
    \__tenkz_geom_xy:nNN {#1}
      \l__tenkz_kernel_r_x_fp \l__tenkz_kernel_r_y_fp
    \__tenkz_geom_xy:nNN {#2}
      \l__tenkz_kernel_r_xb_fp \l__tenkz_kernel_r_yb_fp
    \__tenkz_kernel_r_carrier_basis:n {#2}
    \tl_set:Ne \l__tenkz_kernel_hull_measure_name_tl
      { tenkz_hull_\int_use:N \g__tenkz_kernel_picture_int _#2 }
    \tl_set:Ne \l__tenkz_kernel_r_b_tl
      { \__tenkz_kernel_node_item:nn {#1} {face} }
    \pgfextractx \l__tenkz_kernel_r_north_x_dim
      {
        \pgfpointanchor
          {\tl_use:N \l__tenkz_kernel_hull_measure_name_tl}
          {\tl_use:N \l__tenkz_kernel_r_b_tl}
      }
    \pgfextracty \l__tenkz_kernel_r_north_y_dim
      {
        \pgfpointanchor
          {\tl_use:N \l__tenkz_kernel_hull_measure_name_tl}
          {\tl_use:N \l__tenkz_kernel_r_b_tl}
      }
    \exp_args:NnV \__tenkz_kernel_r_pill_trim_anchor:nn {#2}
      \l__tenkz_kernel_r_b_tl
    \fp_set:Nn \l__tenkz_kernel_r_xc_fp
      {
        \dim_to_fp:n { \l__tenkz_kernel_r_north_x_dim }
        / \dim_to_fp:n { \use:c {tenkz@pitch} }
      }
    \fp_set:Nn \l__tenkz_kernel_r_yc_fp
      {
        \dim_to_fp:n { \l__tenkz_kernel_r_north_y_dim }
        / \dim_to_fp:n { \use:c {tenkz@pitch} }
      }
    \exp_args:NV \__tenkz_kernel_r_face_side:n
      \l__tenkz_kernel_r_b_tl
    \str_case:Vn \l__tenkz_kernel_r_face_side_tl
      {
        {n} { \__tenkz_kernel_r_skin_wire_slot_x: }
        {s} { \__tenkz_kernel_r_skin_wire_slot_x: }
        {e} { \__tenkz_kernel_r_skin_wire_slot_y: }
        {w} { \__tenkz_kernel_r_skin_wire_slot_y: }
      }
    \__tenkz_geom_basis_apply:nnnnnnNN
      { \l__tenkz_kernel_r_basis_ex_tl }
      { \l__tenkz_kernel_r_basis_ey_tl }
      { \l__tenkz_kernel_r_basis_nx_tl }
      { \l__tenkz_kernel_r_basis_ny_tl }
      { \l__tenkz_kernel_r_xc_fp } { \l__tenkz_kernel_r_yc_fp }
      \l__tenkz_kernel_r_x_tl \l__tenkz_kernel_r_y_tl
    \fp_set:Nn \l__tenkz_kernel_r_hp_out_x_fp
      { \l__tenkz_kernel_r_xb_fp + \l__tenkz_kernel_r_x_tl }
    \fp_set:Nn \l__tenkz_kernel_r_hp_out_y_fp
      { \l__tenkz_kernel_r_yb_fp + \l__tenkz_kernel_r_y_tl }
    \fp_set_eq:NN \l__tenkz_kernel_r_x_fp \l__tenkz_kernel_r_hp_save_x_fp
    \fp_set_eq:NN \l__tenkz_kernel_r_y_fp \l__tenkz_kernel_r_hp_save_y_fp
    \fp_set_eq:NN \l__tenkz_kernel_r_xb_fp \l__tenkz_kernel_r_hp_save_xb_fp
    \fp_set_eq:NN \l__tenkz_kernel_r_yb_fp \l__tenkz_kernel_r_hp_save_yb_fp
    \fp_set_eq:NN #3 \l__tenkz_kernel_r_hp_out_x_fp
    \fp_set_eq:NN #4 \l__tenkz_kernel_r_hp_out_y_fp
  }
\cs_generate_variant:Nn \__tenkz_kernel_r_hull_port_xy:nnNN { nVNN, VVNN }

\cs_new_protected:Npn \__tenkz_kernel_r_port:n #1
  {
    \__tenkz_kernel_r_port_record:nN {#1} \l__tenkz_kernel_r_tl
    \quark_if_no_value:NTF \l__tenkz_kernel_r_tl
      {
        \msg_error:nnee {tenkz}{kernel-render-crossref}
          { \__tenkz_kernel_r_item:nn {#1} {text} } { }
      }
      {
        \exp_args:Nx \__tenkz_kernel_atom_rc:nNNN
          { \l__tenkz_kernel_r_tl }
          \l__tenkz_kernel_r_row_tl \l__tenkz_kernel_r_col_tl \l_tmpa_bool
        \bool_if:NTF \l_tmpa_bool
          {
            \__tenkz_kernel_r_span:nNN { \tl_use:N \l__tenkz_kernel_r_tl }
              \l__tenkz_kernel_r_b_tl \l__tenkz_kernel_r_c_tl
            \exp_args:Ne \__tenkz_kernel_r_face_side:n
              { \__tenkz_kernel_r_item:nn {#1} {face} }
            \__tenkz_geom_place_cell:nnee {#1} {kpic}
              {
                \str_case:VnF \l__tenkz_kernel_r_face_side_tl
                  {
                    {n}{ \l__tenkz_kernel_r_row_tl }
                    {s}
                      {
                        \fp_eval:n
                          {
                            \l__tenkz_kernel_r_row_tl
                            + \l__tenkz_kernel_r_c_tl - 1
                          }
                      }
                  }
                  {
                    \fp_eval:n
                      {
                        \l__tenkz_kernel_r_row_tl
                        + \__tenkz_kernel_r_item:nn {#1} {slot} - 1
                      }
                  }
              }
              {
                \str_case:VnF \l__tenkz_kernel_r_face_side_tl
                  {
                    {e}
                      {
                        \fp_eval:n
                          {
                            \l__tenkz_kernel_r_col_tl
                            + \l__tenkz_kernel_r_b_tl - 1
                          }
                      }
                    {w}{ \l__tenkz_kernel_r_col_tl }
                  }
                  {
                    \fp_eval:n
                      {
                        \l__tenkz_kernel_r_col_tl
                        + \__tenkz_kernel_r_item:nn {#1} {slot} - 1
                      }
                  }
              }
          }
          {
            % A non-cell atom owns every slot its own measured extent
            % declares: the slot walks the span symmetrically about the
            % atom's centre, along its local span axis, at the same cell
            % step a frame-anchored spanning atom gives its slots.  The
            % glyph anchor still supplies the face-normal coordinate
            % during ink.
            \__tenkz_kernel_r_span:nNN { \tl_use:N \l__tenkz_kernel_r_tl }
              \l__tenkz_kernel_r_b_tl \l__tenkz_kernel_r_c_tl
            \exp_args:Ne \__tenkz_kernel_r_face_side:n
              { \__tenkz_kernel_r_item:nn {#1} {face} }
            \exp_args:Ne \__tenkz_kernel_r_turn:nN
              { \tl_use:N \l__tenkz_kernel_r_tl } \l__tenkz_kernel_r_row_tl
            \str_case:VnTF \l__tenkz_kernel_r_face_side_tl
              {
                {e} { }
                {w} { }
              }
              {
                % e/w faces: the slot walks the rows, along local south
                \__tenkz_geom_place_rel:nVee {#1} \l__tenkz_kernel_r_tl
                  { \fp_eval:n { \l__tenkz_kernel_r_row_tl - 90 } }
                  {
                    \fp_eval:n
                      {
                        \__tenkz_kernel_r_item:nn {#1} {slot}
                        - ( \l__tenkz_kernel_r_c_tl + 1 ) / 2
                      }
                  }
              }
              {
                % n/s faces: the slot walks the columns, along local east
                \__tenkz_geom_place_rel:nVee {#1} \l__tenkz_kernel_r_tl
                  { \tl_use:N \l__tenkz_kernel_r_row_tl }
                  {
                    \fp_eval:n
                      {
                        \__tenkz_kernel_r_item:nn {#1} {slot}
                        - ( \l__tenkz_kernel_r_b_tl + 1 ) / 2
                      }
                  }
              }
          }
      }
  }

% An outside address sits one clear step beyond the outermost cell of its
% side, at the transverse centre of the declared grid.
\cs_new_protected:Npn \__tenkz_kernel_r_outside:n #1
  {
    \tl_set:Ne \l__tenkz_kernel_r_row_tl
      { \fp_eval:n { ( 1 + \l__tenkz_kernel_r_rows_int ) / 2 } }
    \tl_set:Ne \l__tenkz_kernel_r_col_tl
      { \fp_eval:n { ( 1 + \l__tenkz_kernel_r_cols_int ) / 2 } }
    \exp_args:Ne \str_case:nn { \__tenkz_kernel_r_item:nn {#1} {side} }
      {
        {w}
          {
            \tl_set:Ne \l__tenkz_kernel_r_col_tl
              { \fp_eval:n { 1 - \__tenkz_metric_ratio:n {outsidegap} } }
          }
        {e}
          {
            \tl_set:Ne \l__tenkz_kernel_r_col_tl
              {
                \fp_eval:n
                  {
                    \l__tenkz_kernel_r_cols_int
                    + \__tenkz_metric_ratio:n {outsidegap}
                  }
              }
          }
        {n}
          {
            \tl_set:Ne \l__tenkz_kernel_r_row_tl
              { \fp_eval:n { 1 - \__tenkz_metric_ratio:n {outsidegap} } }
          }
        {s}
          {
            \tl_set:Ne \l__tenkz_kernel_r_row_tl
              {
                \fp_eval:n
                  {
                    \l__tenkz_kernel_r_rows_int
                    + \__tenkz_metric_ratio:n {outsidegap}
                  }
              }
          }
      }
    \__tenkz_geom_place_cell:nnee {#1} {kpic}
      { \tl_use:N \l__tenkz_kernel_r_row_tl }
      { \tl_use:N \l__tenkz_kernel_r_col_tl }
  }


\ExplSyntaxOff
\endinput
