<!-- APEX DOOR · designation=riptoprint.com · series=260901-3dd32a3b · incept=2026-09-01T22:38:09Z · config=lander.json -->
<!doctype html>
<html lang="en" data-mode="light" class="wrap has-top">
<head><meta name="door-template" content="lander.template.html"><meta name="door-series" content="260901-3dd32a3b"><meta name="door-incept" content="2026-09-01T22:38:09Z">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
<title>RIPtoPRINT</title>
<meta name="description" content="RIPtoPRINT routes print work across a distributed node network — preflight before the media is burned, the right queue on the right node, and a receipt that proves what printed. We do not rasterize; we drive the thing that does.">
<link rel="canonical" href="https://riptoprint.com/">
<!-- THEME_COLOR emits the whole <meta> element, not a colour value. Wrapping it
     in a meta tag produced a nested tag, which closed <head> at element 9 and
     dropped twelve elements into <body>. Bare token, on its own line. -->
<meta name="theme-color" content="#101010">
<meta property="og:title" content="RIPtoPRINT">
<meta property="og:description" content="RIPtoPRINT routes print work across a distributed node network — preflight before the media is burned, the right queue on the right node, and a receipt that proves what printed. We do not rasterize; we drive the thing that does.">
<meta property="og:url" content="https://riptoprint.com/">
<meta property="og:type" content="website">
<meta property="og:site_name" content="RIPtoPRINT">
<meta name="twitter:card" content="summary_large_image">
<link rel="icon" href="/icon.svg" type="image/svg+xml">
<link rel="manifest" href="/manifest.webmanifest">
<!-- The JSONLD token emits BARE JSON, not a tag. Left naked in head it is raw
     text: the parser closes head early and every meta/link/style after it lands
     in body, which measured as a phantom 110px overflow at 390. So it is wrapped.
     And do NOT write a token name inside a comment - the serializer substitutes
     there too, which is how this very comment swallowed the whole JSON-LD. -->
<script type="application/ld+json">{"@context":"https://schema.org","@type":"WebSite","name":"RIPtoPRINT","url":"https://riptoprint.com/","description":"RIPtoPRINT routes print work across a distributed node network \u2014 preflight before the media is burned, the right queue on the right node, and a receipt that proves what printed. We do not rasterize; we drive the thing that does.","publisher":{"@type":"Organization","name":"Studio X","url":"https://riptoprint.com"},"potentialAction":[{"@type":"ReadAction","target":"https://riptoprint.com/index.md"}]}</script>
<link rel="alternate" type="text/plain" href="/llms.txt" title="AI front door">
<style>
/* ══════════════════════════════════════════════════════════════════════════
   lander.template.html — THE FOURTH CAST (260901)

   KAI: "can we create a simple on page apex skin that rides as the main
   landing page as a single page site design and navigable basic web state
   page? And have the RAIL-WORKS portion act as the deeper interactive layer
   … the Front Matter (Main Landing Page) should be sparse and have the Back
   Matter state included in that page … and the rear matter in the apex lander
   at the base … once the user lands if they want to go deeper into a site we
   build then the RAIL-WORKS experience becomes the more app like useful
   dashboard bit."

   WHY IT EXISTS. door.template.html is an INSTRUMENT — rails, tabs, a state
   machine, a gear. That is right for someone working, and it is a lot of
   surface for someone who arrived thirty seconds ago wanting a phone number.
   agency.template.html sells. THIS one just RECEIVES: it is the flat, quiet,
   scroll-once page a first-time client lands on, and it hands them exactly one
   door deeper.

   SAME TOKENS, SAME JSON, DIFFERENT CAST — the rule agency.template.html set
   on 260814 and this cast obeys. It restyles the serializer's OWN markup:
   status[] is still .st > .dot + .n > .d + .tag, family[] is still
   a > .n + .d, footer[] is still <p>. NOTHING in serialize-door.sh changed to
   support this mold. Re-emitting markup is the expensive half of a design
   system; restyling emitted markup is the cheap half.

   FM-BM, LITERALLY. The page is Front Matter — who, what, what works. The
   Back Matter is not a separate document and not a hidden panel: it is at the
   BASE OF THIS PAGE, in the open, carrying the build stamp, the template it
   was cast from and the moment it was serialized. What the page claims, and
   what produced the page, arrive together.

   ⚠ DO NOT NAME A CONTAINER .wrap OR .has-top. serialize-door.sh rewrites the
   <html> tag and stamps DOOR classes onto it — this mold's first build came out
   as <html class="wrap has-top">, so a .wrap rule with max-width and padding
   applied to the ROOT ELEMENT. body measured 346px inside a 390px viewport and
   the page scrolled sideways. The container here is .hold for that reason.

   PAPER BY DEFAULT. A first-touch page is a sheet, so this cast starts light
   and follows the reader's system preference — the door's tokens still supply
   every colour, so a tenant carries its own art direction either way.
   ══════════════════════════════════════════════════════════════════════════ */
:root{
  --accent:#ff5e1f; --accent-d:#c44818;
  --paper:#ffffff; --ground:#f9f9f9; --band:#f1f1f1; --line:#d1d1d1;
  --ink:#212121; --ink-deep:#121212; --mut:#575757; --faint:#757575;
  --disp:"Haettenschweiler","Anton","Arial Narrow","Franklin Gothic Heavy",Impact,sans-serif;
  --sans:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Helvetica,Arial,sans-serif;
  --mono:ui-monospace,"SF Mono",Menlo,Consolas,monospace;
  color-scheme:light dark;
}
@media (prefers-color-scheme:dark){
  :root:not([data-mode="light"]){
    --paper:#171717; --ground:#101010; --band:#202020; --line:#2e2e2e;
    --ink:#f5f5f5; --ink-deep:#f5f5f5; --mut:#707070; --faint:#9c9c9c;
  }
}
:root[data-mode="dark"]{
  --paper:#171717; --ground:#101010; --band:#202020; --line:#2e2e2e;
  --ink:#f5f5f5; --ink-deep:#f5f5f5; --mut:#707070; --faint:#9c9c9c;
}
*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
/* NO overflow-x:hidden. It hides a real bug instead of fixing it, and it lies
   to the measurement: with body's overflow propagated to the viewport, this
   page reported documentElement.scrollWidth 501 at a 390px viewport while
   NOTHING was actually wider than 390 and the page could not be scrolled
   sideways at all. A phantom overflow costs more hours than a real one. */
body{margin:0;background:var(--ground);color:var(--ink);
  font:400 16.5px/1.62 var(--sans);-webkit-font-smoothing:antialiased}
/* NOT .wrap -- see the collision note above the markup. */
.hold{max-width:1080px;margin:0 auto;padding:0 22px}
a{color:inherit}
:focus-visible{outline:2.5px solid var(--accent);outline-offset:3px}

/* ── ink density bar: the sheet's edge, five steps of one colour ─────────── */
.ink{display:flex;height:5px}
.ink i{flex:1;background:var(--accent)}
.ink i:nth-child(2){opacity:.72}.ink i:nth-child(3){opacity:.5}
.ink i:nth-child(4){opacity:.3}.ink i:nth-child(5){opacity:.14}

/* ── masthead ────────────────────────────────────────────────────────────── */
header{background:var(--paper);border-bottom:1px solid var(--line);
  position:sticky;top:0;z-index:8}
.mast{display:flex;align-items:center;gap:14px;padding:13px 0;flex-wrap:wrap}
.mk{width:38px;height:38px;border-radius:50%;flex:none;object-fit:cover;
  background:var(--band)}
.who{min-width:0;flex:1}
.who b{display:block;font:800 17.5px/1.15 var(--sans);letter-spacing:-.012em;color:var(--ink-deep)}
.who span{display:block;font:600 10.5px/1.5 var(--mono);letter-spacing:.11em;
  text-transform:uppercase;color:var(--mut);margin-top:2px}
.deep{flex:none;font:700 11.5px/1 var(--mono);letter-spacing:.1em;text-transform:uppercase;
  text-decoration:none;color:var(--paper);background:var(--accent-d);
  padding:11px 14px;border-radius:6px;white-space:nowrap}
.deep:hover{background:var(--accent)}

/* ── page nav: the "basic web state" row, anchors only ───────────────────── */
/* It WRAPS, it does not scroll. An overflow-x:auto strip here made the whole
   document 522px wide at a 390px viewport -- the scroll container leaked. Four
   short anchors have no business being a scroll region anyway. */
.pnav{background:var(--paper);border-bottom:1px solid var(--line)}
.pnav ul{display:flex;flex-wrap:wrap;gap:0 2px;margin:0;padding:0;list-style:none}
.pnav a{display:block;padding:11px 14px;text-decoration:none;white-space:nowrap;
  font:700 11.5px/1 var(--mono);letter-spacing:.1em;text-transform:uppercase;color:var(--mut)}
.pnav a:hover{color:var(--accent-d);background:var(--band)}

/* ── hero ────────────────────────────────────────────────────────────────── */
.hero{background:var(--paper);padding:34px 0 40px}
.heroart{display:block;width:100%;height:auto;border-radius:4px;margin:0 0 26px}
.chip{display:inline-block;font:700 10.5px/1 var(--mono);letter-spacing:.11em;
  text-transform:uppercase;background:var(--accent);color:#0b1416;
  padding:7px 10px;border-radius:4px;margin:0 0 16px}
h1{margin:0;font:400 clamp(38px,7.4vw,74px)/.94 var(--disp);text-transform:uppercase;
  letter-spacing:.004em;color:var(--ink-deep);text-wrap:balance}
h1 span{display:block;color:var(--accent-d)}
.lede{margin:18px 0 0;max-width:60ch;font-size:clamp(16.5px,2vw,19px);color:var(--mut)}

/* ── sections ────────────────────────────────────────────────────────────── */
section{padding:34px 0}
.kick{margin:0 0 15px;font:700 11px/1 var(--mono);letter-spacing:.14em;
  text-transform:uppercase;color:var(--accent-d)}
h2{margin:0 0 4px;font:400 clamp(24px,3.6vw,36px)/1.06 var(--disp);
  text-transform:uppercase;color:var(--ink-deep)}

/* status[] — the serializer's markup, restyled flat */
.board{background:var(--paper);border:1px solid var(--line);border-radius:10px;
  margin-top:14px;overflow:hidden}
.st{display:grid;grid-template-columns:auto 1fr auto;gap:0 12px;align-items:start;
  padding:15px 17px;border-top:1px solid var(--line)}
.st:first-child{border-top:0}
.dot{width:9px;height:9px;border-radius:50%;background:var(--faint);margin-top:7px}
.dot.on{background:var(--accent-d)}
.st .n{font:700 16px/1.35 var(--sans);color:var(--ink-deep);min-width:0}
.st .d{display:block;font:400 14.8px/1.5 var(--sans);color:var(--mut);margin-top:3px}
.st .tag{font:700 10px/1 var(--mono);letter-spacing:.1em;text-transform:uppercase;
  color:var(--faint);white-space:nowrap;margin-top:6px}

/* family[] — a > .n + .d */
.fam{display:grid;gap:9px;margin-top:14px;
  grid-template-columns:repeat(auto-fill,minmax(250px,1fr))}
.fam a{display:block;padding:14px 15px;background:var(--paper);text-decoration:none;
  border:1px solid var(--line);border-left:3px solid var(--accent);border-radius:8px}
.fam a:hover{background:var(--band)}
.fam .n{display:block;font:700 15px/1.3 var(--sans);color:var(--ink-deep)}
.fam .d{display:block;font:400 13.8px/1.45 var(--sans);color:var(--mut);margin-top:3px}
.fam a.off{opacity:.55}

.prose{max-width:64ch;color:var(--mut)}
/* body[] is optional across the estate. An empty prose block would leave a gap
   and a nav anchor pointing at nothing, so the section collapses when empty. */
.prose-sec:has(.prose:empty){padding:0}
.prose blockquote{margin:0 0 16px;padding:0 0 0 16px;border-left:3px solid var(--accent);
  font-size:18px;color:var(--ink-deep)}
.prose b,.prose strong{color:var(--ink-deep)}

/* ── the one door deeper ─────────────────────────────────────────────────── */
.deeper{background:var(--paper);border:1px solid var(--line);
  border-radius:12px;padding:24px 22px;display:flex;gap:18px;
  align-items:center;justify-content:space-between;flex-wrap:wrap}
.deeper h2{margin:0}
.deeper p{margin:6px 0 0;color:var(--mut);max-width:52ch}
.deeper .go{flex:none;text-decoration:none;background:var(--accent-d);color:var(--paper);
  font:700 12.5px/1 var(--mono);letter-spacing:.1em;text-transform:uppercase;
  padding:15px 20px;border-radius:7px}
.deeper .go:hover{background:var(--accent)}

/* ══ BACK MATTER TRAY ════════════════════════════════════════════════════
   KAI 260901: "the footer should be a sticky footer tray that expands when
   clicked - it should be blocked as such for ease of modification."

   COLLAPSED it is one strip: who powers the site, and a cue. EXPANDED it is
   the record. The affordance is the door's own -- .rcue, aria-expanded, and a
   180deg flip on open -- so someone who has used the works recognises it here.

   EVERY PART IS A NUMBERED BLOCK, in the CSS and in the markup, in the same
   order. To change one thing, find its number and change it there only.
   ═══════════════════════════════════════════════════════════════════════ */

/* BLOCK 0 · the shell -- fixed to the bottom, above everything, safe-area aware */
.bmt{position:fixed;left:0;right:0;bottom:0;z-index:40;background:var(--band);
  border-top:1px solid var(--line);box-shadow:0 -2px 14px rgba(24,33,31,.07)}
body{padding-bottom:var(--tray-h,58px)}   /* nothing hides behind the strip */

/* BLOCK 1 · the base strip -- always visible, always clickable */
.bmt-base{display:flex;align-items:center;gap:14px;cursor:pointer;
  padding:0 22px;max-width:1080px;margin:0 auto;min-height:56px}
.bmt-pw{flex:1;min-width:0;font:600 11.5px/1.7 var(--mono);letter-spacing:.06em;
  color:var(--mut)}
.bmt-pw a{color:var(--accent-d);text-decoration:none;border-bottom:1px solid transparent}
.bmt-pw a:hover{border-bottom-color:var(--accent-d)}
.bmt-pw .sep{color:var(--faint);padding:0 5px}

/* BLOCK 2 · the cue -- the door's primitive, same flip, same meaning */
.bmt-cue{display:flex;align-items:center;gap:9px;flex:none}
.bmt-cue .lbl{font:700 10.5px/1 var(--mono);letter-spacing:.14em;
  text-transform:uppercase;color:var(--faint)}
.rcue{display:inline-flex;align-items:center;justify-content:center;flex:none;
  width:30px;height:30px;border:1px solid var(--line);border-radius:50%;
  background:var(--paper);color:var(--mut);cursor:pointer;font-size:11px;
  transition:transform .28s ease,color .2s,border-color .2s}
.rcue:hover{color:var(--accent-d);border-color:var(--accent-d)}
.bmt.open .rcue{transform:rotate(180deg);color:var(--accent-d);border-color:var(--accent-d)}

/* BLOCK 3 · the drawer -- collapsed to nothing, opens upward, scrolls if tall */
.bmt-body{max-height:0;overflow:hidden;transition:max-height .34s cubic-bezier(.32,.72,0,1)}
.bmt.open .bmt-body{max-height:min(66dvh,520px);overflow-y:auto}
.bmt-in{padding:20px 22px 26px;max-width:1080px;margin:0 auto;
  border-top:1px solid var(--line)}

/* BLOCK 4 · a record entry -- label above, content below */
.rec{margin:0 0 16px}
.rec:last-child{margin-bottom:0}
.rec .lbl{margin:0 0 5px;font:700 10px/1 var(--mono);letter-spacing:.16em;
  text-transform:uppercase;color:var(--faint)}
.rec p{margin:0 0 6px;font:500 13.4px/1.62 var(--sans);color:var(--mut);max-width:78ch}
.rec p:last-child{margin-bottom:0}
.rec .bld{font:600 11.5px/1.7 var(--mono);color:var(--faint);word-break:break-word}
.rec .bld b{color:var(--mut);font-weight:700}
.rec a{color:var(--accent-d)}

@media (prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important}}
</style>
</head>
<body>

<div class="ink"><i></i><i></i><i></i><i></i><i></i></div>

<header>
  <div class="hold mast">
    <img class="mk" src="/icon.svg" alt="" width="38" height="38">
    <span class="who"><b>riptoprint.com</b><span>ROUTE · PREFLIGHT · PROVE</span></span>
    <a class="deep" href="/works/">Open the works</a>
  </div>
</header>

<nav class="pnav" aria-label="This page">
  <div class="hold"><ul>
    <li><a href="#works-today">What works</a></li>
    <li><a href="#about">About</a></li>
    <li><a href="#family">The works</a></li>
    <li><a href="#backmatter">Record</a></li>
  </ul></div>
</nav>

<div class="hero">
  <div class="hold">
    
    <span class="chip">riptoprint_com r01.v01</span>
    <h1>From RIP,<span>to print.</span></h1>
    <p class="lede">Every printer already has a RIP — the CUPS filter chain plus the device firmware. That layer works. What is missing, on every shop floor including ours, is the layer around it: preflight before media is burned, routing to the right queue on the right node out of many, transport that gets a file to another state intact, and a receipt that proves what printed and what it cost.</p>
  </div>
</div>

<main>
  <section id="works-today" class="hold">
    <p class="kick">The state of it</p>
    <h2>What works today</h2>
    <div class="board"><div class="st"><span class="dot work"></span><span class="n">Preflight<span class="d">Page box measured, over-width flagged, pages and ink checked before media is burned. Proven single-node in hotfolder.sh, 260828.</span></span><span class="tag">Proven</span></div><div class="st"><span class="dot work"></span><span class="n">Receipts<span class="d">What printed, when, where, at what size, at what cost. TSV log today — single node.</span></span><span class="tag">Proven</span></div><div class="st"><span class="dot on"></span><span class="n">The devices<span class="d">9 output devices across 5 process types on ONE node — wide-format inkjet, film separation, DTG, sublimation, laser and thermal.</span></span><span class="tag">Live</span></div><div class="st"><span class="dot work"></span><span class="n">Gang packing<span class="d">24 order lines → 132 transfers → 21 sheets at 77% mean coverage, 0 overlaps, and a pick manifest row on every single piece.</span></span><span class="tag">Proven</span></div><div class="st"><span class="dot off"></span><span class="n">Routing<span class="d">Hardcoded to one queue today. The node model is specced and the picker is not built yet.</span></span><span class="tag">Soon</span></div><div class="st"><span class="dot off"></span><span class="n">Transport<span class="d">Google Drive Shared Drive + rclone ruled 260829. Not built. Key auth into the Las Vegas node is still open — that one is a human&#x27;s hand.</span></span><span class="tag">Not yet</span></div><div class="st"><span class="dot off"></span><span class="n">Nesting<span class="d">Filling the unused roll width beside a narrow face. Identified, costed, not built.</span></span><span class="tag">Not yet</span></div><div class="st"><span class="dot off"></span><span class="n">Online ordering<span class="d">Nothing here sells anything. This is floor tooling, not a storefront.</span></span><span class="tag">Not yet</span></div></div>
  </section>

  <section id="about" class="hold prose-sec">
    <div class="prose"></div>
  </section>

  <section id="family" class="hold">
    <p class="kick">Related</p>
    <h2>The works</h2>
    <div class="fam"><a class="" href="https://rail-works.com"><span class="n">Rail-Works</span><span class="d">The suite this door is cut from</span></a><a class="" href="https://printshop.link"><span class="n">PrintShop.link</span><span class="d">The print network this tooling drives</span></a><a class="" href="https://unionmadeservices.com"><span class="n">Union Made Services</span><span class="d">The shop — sites, storefronts and tools for locals</span></a><a class="" href="https://unionmadetrades.com"><span class="n">Union Made Trades</span><span class="d">The register of union, allied and union-supporting businesses across every trade</span></a></div>
  </section>

  <section class="hold">
    <div class="deeper">
      <div>
        <h2>Go deeper</h2>
        <p>This page is the front of it. The full Rail-Works view carries every
           sheet, the rail, the state of each job, and a key to the back of house.</p>
      </div>
      <a class="go" href="/works/">The works &rarr;</a>
    </div>
  </section>
</main>

<!-- ══ BACK MATTER TRAY · blocks numbered to match the CSS above ══════════ -->
<footer class="bmt" id="backmatter">

  <!-- BLOCK 1+2 · the base strip. Not a <button>: it carries real links, and
       interactive content cannot nest inside a button. The strip listens for
       clicks and ignores any that landed on an <a>. -->
  <div class="bmt-base" id="bmtbase">
    
    <span class="bmt-cue">
      <span class="lbl">Record</span>
      <button class="rcue" id="bmtcue" type="button" aria-expanded="false"
              aria-controls="bmtbody" aria-label="Show the record">&#9650;</button>
    </span>
  </div>

  <!-- BLOCK 3 · the drawer -->
  <div class="bmt-body" id="bmtbody">
    <div class="bmt-in">

      <!-- BLOCK 4a · THE SHOP — comes from footer[] in the door config. Those
           entries are html-ESCAPED by the serializer, so they are plain text;
           put links in family[] instead. This block also carries the build
           stamp, which the serializer appends to footer[] itself. -->
      <div class="rec">
        <p class="lbl">The shop</p>
        <p>RIPtoPRINT · production routing for a distributed print network · Riverside, California</p><p>We do not rasterize. We drive the thing that does.</p><p class="bld">build <b>260901-3dd32a3b</b> · template lander.template.html · serialized 2026-09-01T22:38:09Z</p>
      </div>

      <!-- BLOCK 4b · THIS PAGE — canon and publisher, from the config. -->
      <div class="rec">
        <p class="lbl">This page</p>
        <p><a href="https://riptoprint.com/">https://riptoprint.com/</a> &middot; published by
           <a href="https://riptoprint.com">Studio X</a></p>
      </div>

    </div>
  </div>
</footer>

<script>
/* The lander ships no state of its own. It reads the one key the door already
   owns (rw.settings.theme) so a reader who set light or dark inside the works
   is not thrown back to system default on the way out. One key, not two. */
/* BLOCK A · theme. One key, the door's own. */
(function(){
  try{
    var t=(JSON.parse(localStorage.getItem('rw.settings')||'{}').theme)||'system';
    if(t==='light'||t==='dark') document.documentElement.setAttribute('data-mode',t);
  }catch(e){}
})();

/* BLOCK B · the back-matter tray.
   Opens on a click anywhere in the strip EXCEPT on a link, on the cue, or on
   the page-nav "Record" anchor. Closes on Escape and on a click outside. The
   collapsed height is measured rather than assumed, because the strip wraps to
   two lines on a narrow phone and a hard-coded padding would clip the footer
   of the page underneath it. */
(function(){
  var bmt=document.getElementById('backmatter'),
      base=document.getElementById('bmtbase'),
      cue=document.getElementById('bmtcue');
  if(!bmt||!base||!cue) return;

  function measure(){
    document.body.style.setProperty('--tray-h', base.offsetHeight+'px');
  }
  measure();
  if(window.ResizeObserver) new ResizeObserver(measure).observe(base);
  else window.addEventListener('resize', measure);

  function set(open){
    bmt.classList.toggle('open', open);
    cue.setAttribute('aria-expanded', open ? 'true' : 'false');
    cue.setAttribute('aria-label', open ? 'Hide the record' : 'Show the record');
  }
  function toggle(){ set(!bmt.classList.contains('open')) }

  base.addEventListener('click', function(e){
    if(e.target.closest('a')) return;      // a link is a link, not a handle
    toggle();
  });
  document.addEventListener('keydown', function(e){ if(e.key==='Escape') set(false) });
  document.addEventListener('click', function(e){
    if(bmt.classList.contains('open') && !bmt.contains(e.target)) set(false);
  });

  /* The page nav's "Record" item opens the tray instead of jumping to a
     fixed element, which scrolling cannot reach. */
  var link=document.querySelector('.pnav a[href="#backmatter"]');
  /* stopPropagation matters: without it this click keeps bubbling to the
     document handler below, which sees a target outside the tray and shuts it
     again in the same tick -- the tray opened and closed invisibly. */
  if(link) link.addEventListener('click', function(e){
    e.preventDefault(); e.stopPropagation(); set(true);
  });
})();
</script>
</body>
</html>
