/* ==========================================================================
   fx.css — imagery, motion, the CAD drawing panel, and the estimator.

   Loaded after styles.css. Everything here is additive: remove this file and
   the site still works, it just stops moving.

   Rules that apply throughout:
     - Motion is decoration. Every animation is off under
       prefers-reduced-motion, and nothing is hidden until JS decides to show
       it unless .js is on <html> (so no-JS users never get a blank page).
     - Colour comes from the tokens in styles.css. The five brand hexes are
       repeated only as a fallback for a browser that somehow loses them.
   ========================================================================== */

@layer components {

  /* ---------------------------------------------------------------------
     A. AERIAL PANELS — real USGS orthoimagery used as ground, never as a
     claim. Text over imagery always sits on a scrim that carries the
     contrast, so the copy passes AA regardless of what is in the photo.
     --------------------------------------------------------------------- */
  .aerial {
    position: relative;
    isolation: isolate;
    overflow: clip;
    background: var(--navy, #1B365D);
  }
  .aerial__img {
    position: absolute;
    inset: 0;
    z-index: -2;
    inline-size: 100%;
    block-size: 100%;
    object-fit: cover;
    /* Desaturated and darkened so it reads as a substrate, not a photo
       competing with the headline. */
    filter: saturate(0.72) contrast(1.05) brightness(0.82);
  }
  .aerial::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background:
      linear-gradient(100deg,
        color-mix(in oklab, var(--navy, #1B365D) 94%, transparent) 0%,
        color-mix(in oklab, var(--navy, #1B365D) 82%, transparent) 46%,
        color-mix(in oklab, var(--navy, #1B365D) 58%, transparent) 100%);
  }
  .aerial--soft::after {
    background: color-mix(in oklab, var(--navy, #1B365D) 76%, transparent);
  }

  /* Hero texture. The hero stays light because that is where the phone
     number and the prices live and they have to stay legible. The aerial
     sits behind at low opacity for texture only, faded out before it
     reaches the copy. */
  .hero--tex { position: relative; isolation: isolate; }
  .hero--tex .hero__tex {
    position: absolute;
    inset-block-start: 0;
    inset-inline: 0;
    block-size: 100%;
    z-index: -1;
    overflow: clip;
    pointer-events: none;
  }
  .hero--tex .hero__tex img {
    inline-size: 100%;
    block-size: 100%;
    object-fit: cover;
    opacity: 0.22;
    filter: saturate(0.4) contrast(1.12);
    translate: 0 calc(var(--par, 0) * 0.6px);
    mask-image: linear-gradient(105deg, transparent 0%, transparent 34%, #000 92%);
  }
  @media (prefers-reduced-motion: reduce) {
    .hero--tex .hero__tex img { translate: none; }
  }

  /* Survey grid ruled over the imagery. Pure CSS, no image request. */
  .aerial__grid {
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    opacity: 0.3;
    background-image:
      linear-gradient(to right, rgba(255, 255, 255, 0.5) 1px, transparent 1px),
      linear-gradient(to bottom, rgba(255, 255, 255, 0.5) 1px, transparent 1px);
    background-size: 96px 96px;
    mask-image: linear-gradient(to bottom, #000 0%, transparent 78%);
  }

  /* Slow drift on the background image as the page scrolls. Driven by JS
     writing --par; falls back to 0 so nothing jumps without it. */
  .aerial__img { translate: 0 calc(var(--par, 0) * 1px); scale: 1.08; }
  @media (prefers-reduced-motion: reduce) {
    .aerial__img { translate: none; scale: 1; }
  }

  /* ---------------------------------------------------------------------
     C. INSTANT ESTIMATOR — the pricing page's working part. Reads the same
     numbers as the published table; the markup carries them in data
     attributes so there is one source of truth per row.
     --------------------------------------------------------------------- */
  .est {
    display: grid;
    gap: var(--sp-6, 1.5rem);
    align-items: start;
  }
  @media (min-width: 48rem) {
    .est { grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); }
  }
  .est__controls { display: grid; gap: var(--sp-5, 1rem); }
  .est__out {
    position: sticky;
    inset-block-start: calc(var(--header-h, 90px) + var(--sp-5, 1rem));
    border: 1px solid var(--rule, #c3cbd8);
    border-radius: var(--r, 4px);
    background: var(--gray-2, #FAFAFA);
    padding: var(--sp-6, 1.5rem);
  }
  .est__label {
    font-family: var(--font-draft, ui-monospace, monospace);
    font-size: var(--step--2, 0.8125rem);
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--fg-muted, #5A5F66);
    display: block;
  }
  .est__fig {
    font-size: clamp(2rem, 1.4rem + 2.6vw, 3rem);
    line-height: 1.05;
    font-weight: 700;
    color: var(--navy, #1B365D);
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.02em;
    margin-block: 0.1em 0.15em;
  }
  .est__note {
    font-size: var(--step--1, 0.875rem);
    color: var(--ink-muted, #5A5F66);
  }
  .est__lines {
    margin-block: var(--sp-5, 1rem) 0;
    border-block-start: 1px solid var(--rule, #c3cbd8);
    padding-block-start: var(--sp-4, 0.75rem);
    display: grid;
    gap: 0.35rem;
    font-size: var(--step--1, 0.875rem);
  }
  .est__line { display: flex; justify-content: space-between; gap: var(--sp-4, 0.75rem); }
  .est__line span:last-child {
    font-family: var(--font-draft, ui-monospace, monospace);
    color: var(--navy, #1B365D);
    white-space: nowrap;
  }
  .est__opts { margin: 0; padding: 0; border: 0; min-inline-size: 0; }
  /* A question that does not apply to the chosen job is dimmed and disabled,
     so the number on the right is always explainable by what is lit up. */
  .est__opts[data-inactive] { opacity: 0.4; }
  .est__opts[data-inactive] .est__chip span { cursor: not-allowed; }
  .est__inactive-note { display: none; }
  .est__opts[data-inactive] .est__inactive-note {
    display: block;
    margin-block-start: var(--sp-3, 0.5rem);
    font-size: var(--step--1, 0.875rem);
    color: var(--fg-muted, #5A5F66);
  }
  .est__chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-3, 0.5rem);
  }
  .est__opts legend {
    font-family: var(--font-draft, ui-monospace, monospace);
    font-size: var(--step--2, 0.8125rem);
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--navy, #1B365D);
    padding: 0;
    margin-block-end: var(--sp-3, 0.5rem);
  }
  .est__chip { position: relative; }
  .est__chip input {
    position: absolute;
    opacity: 0;
    inline-size: 100%;
    block-size: 100%;
    margin: 0;
    cursor: pointer;
  }
  .est__chip span {
    display: block;
    padding: 0.5em 0.9em;
    border: 1px solid var(--field-line, #7d8ba3);
    border-radius: 999px;
    font-size: var(--step--1, 0.875rem);
    background: var(--paper, #fff);
    transition: background .15s, border-color .15s, color .15s;
  }
  .est__chip input:checked + span {
    background: var(--navy, #1B365D);
    border-color: var(--navy, #1B365D);
    color: #fff;
  }
  .est__chip input:focus-visible + span {
    outline: 3px solid var(--navy, #1B365D);
    outline-offset: 2px;
  }
  /* On a touch device these chips are the main control on the page, so give
     them a 44px target rather than the 36px the type alone produces. */
  @media (pointer: coarse) {
    .est__chip span {
      min-block-size: 44px;
      display: flex;
      align-items: center;
      padding-inline: 1.1em;
    }
  }
  @media (prefers-reduced-motion: reduce) {
    .est__chip span { transition: none; }
  }

  /* ---------------------------------------------------------------------
     D. SCROLL REVEAL + COUNT-UP. Both are opt-in per element and both are
     inert unless <html class="js">, so nothing is ever stuck invisible.
     --------------------------------------------------------------------- */
  .js .fx-up {
    opacity: 0;
    translate: 0 14px;
  }
  .js .fx-up.is-in {
    opacity: 1;
    translate: none;
    transition: opacity .55s ease var(--d, 0ms), translate .55s cubic-bezier(.22,.61,.36,1) var(--d, 0ms);
  }
  @media (prefers-reduced-motion: reduce) {
    .js .fx-up { opacity: 1; translate: none; }
    .js .fx-up.is-in { transition: none; }
  }

  /* ---------------------------------------------------------------------
     E. CHAINAGE BAR — scroll progress, labelled the way a plan sheet
     stations a centerline. Decorative, hidden from assistive tech.
     --------------------------------------------------------------------- */
  .chainage {
    position: fixed;
    inset-block-start: 0;
    inset-inline: 0;
    block-size: 3px;
    z-index: 60;
    pointer-events: none;
    background: transparent;
  }
  .chainage__fill {
    block-size: 100%;
    inline-size: calc(var(--p, 0) * 100%);
    background: linear-gradient(90deg,
      var(--navy, #1B365D), var(--orange, #E87722));
    transform-origin: left;
  }
  .chainage__sta {
    position: fixed;
    inset-block-start: 8px;
    inset-inline-end: 10px;
    font-family: var(--font-draft, ui-monospace, monospace);
    font-size: 11px;
    letter-spacing: 0.12em;
    color: var(--ink-muted, #5A5F66);
    background: color-mix(in oklab, var(--paper, #fff) 84%, transparent);
    padding: 2px 6px;
    border-radius: 2px;
    z-index: 60;
    pointer-events: none;
  }
  @media (max-width: 48rem) { .chainage__sta { display: none; } }
  @media print { .chainage, .chainage__sta { display: none; } }

  /* ---------------------------------------------------------------------
     F. IMAGE CARD — an aerial with a caption, used to head a section.
     --------------------------------------------------------------------- */
  .shot { margin: 0; position: relative; }
  /* These head a section, so they hold one band height whatever shape the
     photograph came off the phone in. Portrait shots are cropped, not
     letterboxed, and each one says where its subject sits in the frame. */
  .shot img {
    inline-size: 100%;
    aspect-ratio: 3 / 2;
    object-fit: cover;
    object-position: center var(--shot-y, 50%);
    display: block;
    border-radius: var(--r, 4px);
    border: 1px solid var(--rule, #c3cbd8);
  }
  @media (max-width: 34rem) { .shot img { aspect-ratio: 4 / 3; } }
  .shot figcaption {
    margin-block-start: var(--sp-3, 0.5rem);
    font-family: var(--font-draft, ui-monospace, monospace);
    font-size: var(--step--2, 0.8125rem);
    letter-spacing: 0.08em;
    color: var(--fg-muted, #5A5F66);
  }
}

@layer components {

  /* ---------------------------------------------------------------------
     G. DRAWING PANEL — a real CAD sheet that plots itself in.

     Geometry is converted from the original DXF, so every line on screen is
     a line that was drafted for a real project. Each <path> carries
     pathLength="1", which normalises them all: one dash rule draws a 4-foot
     lot line and a 1,600-foot centerline at the same rate.
     --------------------------------------------------------------------- */
  .dwg { margin: 0; }
  .dwg__frame {
    position: relative;
    overflow: hidden;
    border: 1px solid color-mix(in oklab, white 22%, transparent);
    border-radius: var(--r, 4px);
    background:
      radial-gradient(120% 90% at 50% 0%,
        color-mix(in oklab, var(--navy, #1B365D) 78%, black) 0%,
        color-mix(in oklab, var(--navy, #1B365D) 92%, black) 70%,
        #0b1626 100%);
  }
  /* Drafting grid, 1 CSS square per notional 50 feet. */
  .dwg__frame::before {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    opacity: .28;
    background-image:
      linear-gradient(to right, rgba(255,255,255,.16) 1px, transparent 1px),
      linear-gradient(to bottom, rgba(255,255,255,.16) 1px, transparent 1px);
    background-size: 44px 44px;
  }
  .dwg .cad { display: block; inline-size: 100%; block-size: auto; }

  /* --- line weights and colours, by drawn group --------------------- */
  .cad-misc, .cad-parcel { stroke: #9db4d0; stroke-width: 1; }
  .cad-row               { stroke: #F08A3C; stroke-width: 1.4; }
  .cad-esmt              { stroke: #d9b64a; stroke-width: 1.1; }
  .cad-pavt              { stroke: #eaf1fa; stroke-width: 1.1; }
  .cad-walk              { stroke: #86c5ff; stroke-width: 1; }
  .cad-light             { stroke: #ffd479; stroke-width: 1.1; }
  .cad-cl                { stroke: #ff7a45; stroke-width: 1.5; }
  .cad-text              { stroke: #7d94b4; stroke-width: .8; }
  .cad-labels {
    fill: #d7e6f8;
    stroke: none;
    font-family: var(--font-draft, ui-monospace, monospace);
    letter-spacing: .04em;
  }
  .cad--road { --dwg-weight: .85; }
  .cad--road [class^="cad-"] { stroke-width: calc(1px * var(--dwg-weight, 1)); }

  /* --- the plot ------------------------------------------------------ */
  .js .dwg .cad path { stroke-dasharray: 1; stroke-dashoffset: 1; }
  .js .dwg .cad-labels { opacity: 0; }

  .js .dwg.is-plotting .cad path {
    animation: dwg-draw 900ms cubic-bezier(.25,.8,.4,1) var(--dl, 0ms) forwards;
  }
  @keyframes dwg-draw { to { stroke-dashoffset: 0; } }

  /* Draw order. Ground first, then the design on top of it. */
  .js .dwg .cad-parcel path,
  .js .dwg .cad-misc   path { --dl: 0ms; }
  .js .dwg .cad-esmt   path { --dl: 420ms; }
  .js .dwg .cad-row    path { --dl: 620ms; }
  .js .dwg .cad-pavt   path { --dl: 900ms; }
  .js .dwg .cad-walk   path { --dl: 1200ms; }
  .js .dwg .cad-cl     path { --dl: 1450ms; }
  .js .dwg .cad-light  path { --dl: 1650ms; }
  .js .dwg .cad-text   path { --dl: 1800ms; }

  .js .dwg.is-plotted .cad-labels {
    opacity: 1;
    transition: opacity 500ms ease;
  }
  /* The centerline is drawn solid, then its linetype snaps in — which is
     the order it happens in on a real drawing. */
  .js .dwg.is-plotted .cad-cl path {
    stroke-dasharray: 26 7 5 7;
    stroke-dashoffset: 0;
  }
  .js .dwg.is-plotted .cad-esmt path { stroke-dasharray: 10 6; }

  /* The plotter head. */
  .dwg__bar {
    position: absolute;
    inset-block: 0;
    inline-size: 2px;
    inset-inline-start: 0;
    opacity: 0;
    background: linear-gradient(to bottom,
      transparent, #ffb27a 12%, #fff 50%, #ffb27a 88%, transparent);
    box-shadow: 0 0 18px 5px rgba(255, 150, 80, .45);
    pointer-events: none;
  }
  .js .dwg.is-plotting .dwg__bar {
    animation: dwg-sweep 2700ms cubic-bezier(.36,.1,.2,1) forwards;
  }
  @keyframes dwg-sweep {
    0%   { inset-inline-start: 0;    opacity: 0; }
    6%   { opacity: 1; }
    92%  { opacity: 1; }
    100% { inset-inline-start: 100%; opacity: 0; }
  }

  /* --- legend -------------------------------------------------------- */
  .dwg__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-4, .75rem);
    margin-block-start: var(--sp-4, .75rem);
  }
  .dwg__layers {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-3, .5rem) var(--sp-5, 1rem);
    font-family: var(--font-draft, ui-monospace, monospace);
    font-size: var(--step--2, .8125rem);
    letter-spacing: .1em;
    text-transform: uppercase;
  }
  .dwg__layers li {
    display: flex;
    align-items: center;
    gap: .5em;
    color: var(--fg-muted, #5A5F66);
    opacity: .45;
    transition: opacity .3s ease, color .3s ease;
  }
  .dwg__layers li::before {
    content: "";
    inline-size: 18px;
    block-size: 0;
    border-block-start: 2px solid currentColor;
    flex: none;
  }
  .dwg__layers li.is-on { opacity: 1; color: var(--fg, #fff); }
  .dwg__layers li[data-g="row"].is-on   { color: #F08A3C; }
  .dwg__layers li[data-g="cl"].is-on    { color: #ff7a45; }
  .dwg__layers li[data-g="esmt"].is-on  { color: #d9b64a; }
  .dwg__layers li[data-g="pavt"].is-on  { color: #eaf1fa; }
  .dwg__layers li[data-g="walk"].is-on  { color: #86c5ff; }
  .dwg__layers li[data-g="cl"]::before  { border-block-start-style: dashed; }
  .dwg__layers li[data-g="esmt"]::before { border-block-start-style: dashed; }

  .dwg__replay {
    font: inherit;
    font-family: var(--font-draft, ui-monospace, monospace);
    font-size: var(--step--2, .8125rem);
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--fg, #fff);
    background: transparent;
    border: 1px solid color-mix(in oklab, currentColor 45%, transparent);
    border-radius: 999px;
    padding: .5em 1em;
    cursor: pointer;
    min-block-size: 36px;
  }
  .dwg__replay:hover { background: color-mix(in oklab, currentColor 12%, transparent); }
  .dwg__replay:focus-visible { outline: 3px solid currentColor; outline-offset: 2px; }

  .dwg figcaption {
    margin-block-start: var(--sp-4, .75rem);
    font-family: var(--font-draft, ui-monospace, monospace);
    font-size: var(--step--2, .8125rem);
    line-height: 1.6;
    letter-spacing: .05em;
    color: var(--fg-muted, #5A5F66);
  }

  /* Motion off: show the finished sheet, keep the legend lit, hide replay. */
  @media (prefers-reduced-motion: reduce) {
    .js .dwg .cad path { stroke-dashoffset: 0; }
    .js .dwg .cad-labels { opacity: 1; }
    .js .dwg.is-plotting .cad path,
    .js .dwg.is-plotting .dwg__bar { animation: none; }
    .dwg__bar { display: none; }
    .dwg__layers li { opacity: 1; }
    .dwg__replay { display: none; }
  }
  @media print {
    .dwg__bar, .dwg__replay { display: none; }
    .js .dwg .cad path { stroke-dashoffset: 0; }
    .js .dwg .cad-labels { opacity: 1; }
  }
}

@layer components {
  /* A 3:1 road corridor squeezed into a phone is 100px tall and unreadable.
     Give it a floor and let the frame scroll sideways instead, which is how
     you look at a plan sheet anyway. */
  .dwg--wide .dwg__frame {
    overflow-x: auto;
    overscroll-behavior-x: contain;
  }
  @media (max-width: 52rem) {
    .dwg--wide .cad { min-inline-size: 760px; }
    .dwg--wide .dwg__frame::after {
      content: "Scroll to follow the corridor \2192";
      position: absolute;
      inset-block-end: 6px;
      inset-inline-end: 8px;
      font-family: var(--font-draft, ui-monospace, monospace);
      font-size: 11px;
      letter-spacing: .08em;
      text-transform: uppercase;
      color: color-mix(in oklab, white 70%, transparent);
      background: rgba(11, 22, 38, .8);
      padding: 3px 7px;
      border-radius: 2px;
      pointer-events: none;
    }
  }
  @media (pointer: coarse) {
    .dwg__replay { min-block-size: 44px; padding-inline: 1.2em; }
  }
}

@layer components {
  /* ---------------------------------------------------------------------
     H. GEOREFERENCED DRAWING — the linework sitting on the ground it was
     drawn for. The aerial is requested for exactly the bounding box the
     vectors are projected into, so the two align to the foot rather than
     being eyeballed into place.
     --------------------------------------------------------------------- */
  .dwg--geo .dwg__frame { background: #0b1626; }
  .dwg--geo .dwg__frame::before { opacity: .1; }   /* grid steps back */
  .dwg--geo .dwg__base {
    display: block;
    inline-size: 100%;
    block-size: auto;
  }
  .dwg--geo .cad {
    position: absolute;
    inset: 0;
    inline-size: 100%;
    block-size: 100%;
  }
  /* Over photography the linework needs a dark edge to stay readable. */
  .dwg--geo [class^="cad-"] { paint-order: stroke; }
  .dwg--geo .cad-pavt  { stroke-width: 1.6; }
  .dwg--geo .cad-row   { stroke-width: 1.6; }
  .dwg--geo .cad-misc,
  .dwg--geo .cad-parcel { stroke: #b9cde6; stroke-width: 1.1; }

  /* --- layer toggles ------------------------------------------------- */
  .dwg__layers button {
    font: inherit;
    display: flex;
    align-items: center;
    gap: .5em;
    background: none;
    border: 0;
    padding: .25em 0;
    color: inherit;
    cursor: pointer;
    letter-spacing: inherit;
    text-transform: inherit;
    min-block-size: 32px;
  }
  .dwg__layers button::before {
    content: "";
    inline-size: 18px;
    block-size: 0;
    border-block-start: 2px solid currentColor;
    flex: none;
  }
  .dwg__layers li::before { content: none; }      /* the button carries it */
  .dwg__layers button:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: 3px;
    border-radius: 2px;
  }
  .dwg__layers button[aria-pressed="false"] {
    opacity: .38;
    text-decoration: line-through;
  }
  .cad .is-off { display: none; }

  @media (pointer: coarse) { .dwg__layers button { min-block-size: 44px; } }
  @media (max-width: 52rem) {
    .dwg--geo.dwg--wide .cad { min-inline-size: 0; }   /* base img sets width */
    .dwg--geo.dwg--wide .dwg__base { min-inline-size: 900px; }
  }
}

@layer components {
  /* ---------------------------------------------------------------------
     I. Layer palette, second pass. The roadway file carries more distinct
     things than the plat does, so each gets its own colour and its own slot
     in the plot order rather than being lumped into "everything else".
     --------------------------------------------------------------------- */
  .cad-exist  { stroke: #8ea4c0; stroke-width: .9; }
  .cad-slope  { stroke: #94a3b5; stroke-width: .9; }
  .cad-drain  { stroke: #6ec1ff; stroke-width: 1.1; }
  .cad-demo   { stroke: #ff7a7a; stroke-width: 1.1; }
  .cad-dwy    { stroke: #c9aeff; stroke-width: 1.1; }
  .cad-curb   { stroke: #7cf0c4; stroke-width: 1.2; }
  .cad-road   { stroke: #ffffff; stroke-width: 1.5; }

  .js .dwg .cad-exist path,
  .js .dwg .cad-slope path  { --dl: 0ms; }
  .js .dwg .cad-demo  path  { --dl: 260ms; }
  .js .dwg .cad-drain path  { --dl: 340ms; }
  .js .dwg .cad-dwy   path  { --dl: 1000ms; }
  .js .dwg .cad-curb  path  { --dl: 1050ms; }
  .js .dwg .cad-road  path  { --dl: 780ms; }

  .dwg__layers li[data-g="road"].is-on  { color: #ffffff; }
  .dwg__layers li[data-g="curb"].is-on  { color: #7cf0c4; }
  .dwg__layers li[data-g="dwy"].is-on   { color: #c9aeff; }
  .dwg__layers li[data-g="demo"].is-on  { color: #ff7a7a; }
  .dwg__layers li[data-g="drain"].is-on { color: #6ec1ff; }
  .dwg__layers li[data-g="exist"].is-on { color: #8ea4c0; }

  /* No aerial behind it: the plat sits on the drafting ground, so its
     linework needs the darker-on-light weighting the panel already has. */
  .dwg:not(.dwg--geo) .cad-misc { stroke: #9db4d0; stroke-width: 1.1; }
  .dwg:not(.dwg--geo) .cad-row  { stroke: #F08A3C; stroke-width: 1.5; }
}

@layer components {
  /* ---------------------------------------------------------------------
     J. PHOTO GALLERY — construction photography, with a lightbox for the
     detail. Built on <dialog>, so Escape, focus trapping and the backdrop
     come from the browser instead of from us.
     --------------------------------------------------------------------- */
  .gallery {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: var(--sp-6, 1.5rem);
    grid-template-columns: 1fr;
  }
  @media (min-width: 40rem) { .gallery { grid-template-columns: repeat(2, 1fr); } }
  @media (min-width: 66rem) { .gallery { grid-template-columns: repeat(3, 1fr); } }

  .gallery .shot { margin: 0; }
  .gallery__btn {
    display: block;
    inline-size: 100%;
    padding: 0;
    border: 0;
    background: none;
    cursor: zoom-in;
    border-radius: var(--r, 4px);
  }
  .gallery__btn img {
    inline-size: 100%;
    aspect-ratio: 4 / 3;
    object-fit: cover;
    object-position: center var(--shot-y, 50%);
    display: block;
    border-radius: var(--r, 4px);
    border: 1px solid var(--rule, #c3cbd8);
    transition: filter .25s ease;
  }
  .gallery__btn:hover img { filter: brightness(1.06) saturate(1.06); }
  .gallery__btn:focus-visible {
    outline: 3px solid var(--focus, #1B365D);
    outline-offset: 3px;
  }
  @media (prefers-reduced-motion: reduce) {
    .gallery__btn img { transition: none; }
  }

  .lightbox {
    border: 0;
    padding: 0;
    max-inline-size: min(96vw, 1400px);
    max-block-size: 94vh;
    background: #0b1626;
    color: #e8eef7;
    border-radius: var(--r, 4px);
    overflow: hidden;
  }
  .lightbox::backdrop { background: rgba(6, 12, 22, .88); }
  .lightbox img {
    display: block;
    inline-size: 100%;
    block-size: auto;
    max-block-size: 82vh;
    object-fit: contain;
    background: #06101c;
  }
  .lightbox__cap {
    padding: var(--sp-4, .75rem) var(--sp-5, 1rem);
    font-family: var(--font-draft, ui-monospace, monospace);
    font-size: var(--step--2, .8125rem);
    line-height: 1.6;
    letter-spacing: .04em;
    color: #b9cbe2;
  }
  .lightbox__close {
    position: absolute;
    inset-block-start: 8px;
    inset-inline-end: 8px;
    inline-size: 44px;
    block-size: 44px;
    border-radius: 50%;
    border: 1px solid rgba(255, 255, 255, .35);
    background: rgba(11, 22, 38, .8);
    color: #fff;
    font-size: 20px;
    line-height: 1;
    cursor: pointer;
  }
  .lightbox__close:hover { background: rgba(255, 255, 255, .16); }
  .lightbox__close:focus-visible { outline: 3px solid #fff; outline-offset: 2px; }
}

@layer components {
  /* ---------------------------------------------------------------------
     K. PLAN AND PROFILE — the corridor over its own ground, the ground line
     under it, one station axis through both.

     The plan comes from three drawings merged (linework, striping, plan and
     profile); the ground line and the stationing come from the LandXML the
     alignment was exported to. Because both panels are driven off the same
     easting, a single rule crossing the sheet reads the same station in each,
     and every position on it is a real one.
     --------------------------------------------------------------------- */

  /* --- what the roadway file carries that the plat does not ---------- */
  .cad--road .cad-exist  { stroke: #7e93ad; stroke-width: .8; }
  .cad--road .cad-slope  { stroke: #8494a8; stroke-width: .8; }
  .cad--road .cad-demo   { stroke: #ff8b8b; stroke-width: 1; }
  .cad--road .cad-esmt   { stroke: #d9b64a; stroke-width: 1; }
  .cad--road .cad-row    { stroke: #F08A3C; stroke-width: 1.3; }
  .cad--road .cad-watr   { stroke: #4fd1c5; stroke-width: 1.1; }
  .cad--road .cad-storm  { stroke: #58b6ff; stroke-width: 1.1; }
  .cad--road .cad-dwy    { stroke: #c9aeff; stroke-width: 1; }
  .cad--road .cad-pavt   { stroke: #ffffff; stroke-width: 1.3; }
  .cad--road .cad-curb   { stroke: #7cf0c4; stroke-width: 1.2; }
  .cad--road .cad-walk   { stroke: #86c5ff; stroke-width: 1.1; }
  .cad--road .cad-light  { stroke: #ffd479; stroke-width: 1; }
  .cad--road .cad-sign   { stroke: #b7c7dd; stroke-width: .9; }
  .cad--road .cad-mark   { stroke: #f4f7fb; stroke-width: 1.1; }
  .cad--road .cad-marky  { stroke: #ffd83d; stroke-width: 1.2; }
  .cad--road .cad-cl     { stroke: #ff7a45; stroke-width: 1.3; }
  .cad--road .cad-sta    { stroke: #ff9a63; stroke-width: 1; }
  .cad--road .cad-stalab {
    fill: #ffc09a;
    stroke: none;
    font-family: var(--font-draft, ui-monospace, monospace);
    font-weight: 600;
    font-size: calc(11px * 4000 / var(--dw, 1152));
    letter-spacing: .06em;
    text-anchor: middle;
    dominant-baseline: central;
  }

  .dwg__layers li[data-g="storm"].is-on { color: #58b6ff; }
  .dwg__layers li[data-g="watr"].is-on  { color: #4fd1c5; }
  .dwg__layers li[data-g="pavt"].is-on  { color: #ffffff; }
  .dwg__layers li[data-g="sign"].is-on  { color: #b7c7dd; }
  .dwg__layers li[data-g="mark"].is-on  { color: #f4f7fb; }
  .dwg__layers li[data-g="marky"].is-on { color: #ffd83d; }
  .dwg__layers li[data-g="light"].is-on { color: #ffd479; }
  .dwg__layers li[data-g="esmt"].is-on  { color: #d9b64a; }
  .dwg__layers li[data-g="cl sta stalab"].is-on { color: #ff9a63; }
  .dwg__layers li[data-g="cl sta stalab"]::before,
  .dwg__layers li[data-g="cl sta stalab"] button::before {
    border-block-start-style: dashed;
  }

  /* --- the sheet ------------------------------------------------------ */
  .sheet { margin: 0; position: relative; }
  /* Below the floor set on .sheet__inner the sheet pans instead of shrinking,
     so say so. Pinned to the figure, not the scroll port, or it pans away. */
  @media (max-width: 68rem) {
    .sheet::after {
      content: "Drag to follow the corridor \2192";
      position: absolute;
      inset-block-start: 6px;
      inset-inline-end: 8px;
      font-family: var(--font-draft, ui-monospace, monospace);
      font-size: 11px;
      letter-spacing: .08em;
      text-transform: uppercase;
      color: color-mix(in oklab, white 72%, transparent);
      background: rgba(9, 19, 33, .82);
      padding: 3px 7px;
      border-radius: 2px;
      pointer-events: none;
      z-index: 5;
    }
  }
  .sheet__scroll {
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior-x: contain;
    scrollbar-width: thin;
    scrollbar-color: color-mix(in oklab, white 30%, transparent) transparent;
  }
  .sheet__scroll:focus-visible {
    outline: 3px solid var(--fg, #fff);
    outline-offset: 3px;
  }
  /* Below this the corridor stops being legible, so the sheet pans instead
     of shrinking — which is how a plan sheet is read anyway. */
  .sheet__inner {
    position: relative;
    min-inline-size: 64rem;
    display: grid;
    gap: 10px;
  }
  .sheet__plan { position: relative; }
  .sheet__prof {
    position: relative;
    overflow: hidden;
    border: 1px solid color-mix(in oklab, white 22%, transparent);
    border-radius: var(--r, 4px);
    background: #091321;
  }
  .prof { display: block; inline-size: 100%; block-size: auto; }
  .prof-grid path        { stroke: #22364e; }
  .prof-grid--major path { stroke: #3c5878; }
  .prof-exist path       { stroke: #dbe7f5; stroke-width: 2; }
  .prof-design path      { stroke: #7cf0c4; stroke-width: 3; }
  .prof-pvi path         { stroke: #7cf0c4; stroke-width: 2.4; }
  .prof-elev, .prof-sta {
    fill: #7f96b4;
    stroke: none;
    font-family: var(--font-draft, ui-monospace, monospace);
    font-weight: 500;
    font-size: calc(11px * 4000 / var(--dw, 1152));
    letter-spacing: .05em;
  }
  .prof-sta { text-anchor: middle; fill: #93a9c4; }

  /* --- the plot: a station sweep ---------------------------------------
     The sheet does not build up layer by layer. It comes off the plotter the
     way a real one does, left to right along the stationing, with the ground
     line arriving underneath at the same station. One clip drives both panels
     and the aerial, so nothing can get out of step with anything else.
     --------------------------------------------------------------------- */
  .js .sheet .cad path,
  .js .sheet .prof-exist path,
  .js .sheet .prof-design path {
    stroke-dasharray: none;
    stroke-dashoffset: 0;
  }
  .js .sheet .cad-stalab,
  .js .sheet .prof-grid,
  .js .sheet .prof-elev,
  .js .sheet .prof-sta,
  .js .sheet .prof-pvi { opacity: 1; }

  .js .sheet .sheet__plan,
  .js .sheet .sheet__prof { clip-path: inset(0 100% 0 0); }
  .js .sheet.is-plotting .sheet__plan,
  .js .sheet.is-plotting .sheet__prof {
    animation: sheet-sweep 3200ms linear forwards;
  }
  @keyframes sheet-sweep {
    from { clip-path: inset(0 100% 0 0); }
    to   { clip-path: inset(0 0 0 0); }
  }
  /* Linear, so the plotter head sits exactly on the edge of what it has drawn. */
  .js .sheet.is-plotting .dwg__bar {
    animation: dwg-sweep 3200ms linear forwards;
  }
  .js .sheet.is-plotted .sheet__plan,
  .js .sheet.is-plotted .sheet__prof { clip-path: none; }
  .js .sheet.is-plotted .cad-esmt path { stroke-dasharray: 8 5; }

  /* --- the station cursor --------------------------------------------- */
  .sheet__rule {
    position: absolute;
    inset-block: 0;
    inline-size: 1px;
    background: repeating-linear-gradient(to bottom,
      rgba(255, 255, 255, .7) 0 6px, transparent 6px 12px);
    pointer-events: none;
    z-index: 3;
  }
  .sheet__dot {
    position: absolute;
    inline-size: 11px;
    block-size: 11px;
    margin: -6px 0 0 -6px;
    border-radius: 50%;
    background: #ff7a45;
    box-shadow: 0 0 0 2px rgba(9, 19, 33, .9);
    pointer-events: none;
    z-index: 4;
  }
  .sheet__dot--prof { background: #7cf0c4; }
  .sheet__rule[hidden], .sheet__dot[hidden] { display: none; }

  .sheet__read {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .3em 1.5em;
    margin-block-start: .75rem;
    font: 500 .82rem/1.5 var(--font-draft, ui-monospace, monospace);
    color: var(--fg-muted, #b9cbe2);
  }
  .sheet__read-k {
    font-size: .74em;
    letter-spacing: .14em;
    text-transform: uppercase;
    opacity: .82;
    margin-inline-end: -1.1em;
  }
  .sheet__read b {
    color: var(--fg, #fff);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
  }

  /* --- curve data ------------------------------------------------------ */
  .sheet__curves {
    list-style: none;
    margin: .9rem 0 0;
    padding: .7rem .9rem;
    border: 1px solid color-mix(in oklab, currentColor 18%, transparent);
    border-radius: var(--r, 4px);
    font: 500 .76rem/1.7 var(--font-draft, ui-monospace, monospace);
    color: var(--fg-muted, #b9cbe2);
    font-variant-numeric: tabular-nums;
  }
  .sheet__curves b {
    color: var(--fg, #fff);
    font-weight: 600;
    letter-spacing: .1em;
    text-transform: uppercase;
    font-size: .86em;
    margin-inline-end: .6em;
  }
  .sheet__curves i { font-style: normal; opacity: .78; }

  @media (max-width: 47.99rem) {
    .sheet__read { gap: .25em 1.1em; font-size: .74rem; }
    .sheet__curves { font-size: .7rem; }
    /* Fifteen switches stacked at 44px each is half a phone screen of legend.
       36px still clears the WCAG 2.2 AA target minimum by half again. */
    .sheet .dwg__layers { gap: 8px 16px; }
    .sheet .dwg__layers button { min-block-size: 36px; font-size: .72rem; }
  }
  @media (prefers-reduced-motion: reduce), print {
    .js .sheet .sheet__plan,
    .js .sheet .sheet__prof { clip-path: none; }
    .js .sheet.is-plotting .sheet__plan,
    .js .sheet.is-plotting .sheet__prof { animation: none; }
  }
  @media print {
    .sheet::after { display: none; }
    .sheet__scroll { overflow: visible; }
  }
}

@layer components {
  /* ---------------------------------------------------------------------
     L. CONTROL DIAGRAM — the boundary and right-of-way survey the corridor
     was set out from, at section scale rather than corridor scale.

     Nothing lettered on the survey is drawn: the sheet carries owner names
     and addresses off the deeds. The only text here is the section numbers
     and the monument keys, both of which are public land survey record.
     --------------------------------------------------------------------- */
  .cad--survey { --dwg-weight: 1; }
  .cad--survey .cad-parcel { stroke: #7f93ae; stroke-width: .9; }
  .cad--survey .cad-subd   { stroke: #b98cf0; stroke-width: 1.1; }
  .cad--survey .cad-eutil  { stroke: #d9b64a; stroke-width: 1; }
  .cad--survey .cad-esmt   { stroke: #b49a52; stroke-width: 1; }
  .cad--survey .cad-eelec  { stroke: #ffd479; stroke-width: 1; }
  .cad--survey .cad-esan   { stroke: #7cf0c4; stroke-width: 1.1; }
  .cad--survey .cad-row    { stroke: #e8a33c; stroke-width: 1.3; }
  .cad--survey .cad-cl     { stroke: #7ea6d8; stroke-width: 1; }
  /* The framework itself, so it reads over everything hung off it. */
  .cad--survey .cad-sect   { stroke: #f2f7fd; stroke-width: 1.7; }
  .cad--survey .cad-align  { stroke: #ff7a45; stroke-width: 2.2; }
  /* What the sheet above covers. Given a wash as well as an outline, or it
     disappears into the section lines it sits between. */
  .cad--survey .cad-sheet  {
    stroke: #ffe9c9;
    stroke-width: 1.6;
    stroke-dasharray: 11 8;
    fill: color-mix(in oklab, #cfe3ff 7%, transparent);
  }
  .cad--survey .cad-mon {
    stroke: #ffd08a;
    stroke-width: 1.5;
    fill: none;
  }
  .cad--survey .cad-mon .is-set { fill: #ffd08a; }
  .cad--survey .cad-monlab {
    fill: #ffd08a;
    stroke: none;
    font-family: var(--font-draft, ui-monospace, monospace);
    font-weight: 700;
    font-size: calc(12px * 2600 / var(--dw, 1152));
  }
  .cad--survey .cad-seclab {
    fill: color-mix(in oklab, #dbe7f5 62%, transparent);
    stroke: none;
    font-family: var(--font-draft, ui-monospace, monospace);
    font-weight: 600;
    font-size: calc(15px * 2600 / var(--dw, 1152));
    letter-spacing: .12em;
    text-anchor: middle;
  }

  .dwg__layers li[data-g="sect"].is-on   { color: #f2f7fd; }
  .dwg__layers li[data-g="mon"].is-on    { color: #ffd08a; }
  .dwg__layers li[data-g="subd"].is-on   { color: #b98cf0; }
  .dwg__layers li[data-g="parcel"].is-on { color: #7f93ae; }
  .dwg__layers li[data-g="eutil"].is-on  { color: #d9b64a; }
  .dwg__layers li[data-g="eelec"].is-on  { color: #ffd479; }
  .dwg__layers li[data-g="esan"].is-on   { color: #7cf0c4; }
  .dwg__layers li[data-g="align"].is-on  { color: #ff7a45; }
  .dwg__layers li[data-g="cl"].is-on     { color: #7ea6d8; }
  .dwg__layers li[data-g="sheet"].is-on  { color: #ffffff; }
  .dwg__layers li[data-g="sheet"] button::before,
  .dwg__layers li[data-g="align"] button::before {
    border-block-start-style: dashed;
  }

  /* --- the plot -------------------------------------------------------
     Scoped to .cad--survey. Left unscoped these would reach the plat and the
     roadway sheet, which share group names but not this running order. */
  .js .dwg .cad--survey .cad-sect   path { --dl: 1000ms; }
  .js .dwg .cad--survey .cad-row    path { --dl: 1400ms; }
  .js .dwg .cad--survey .cad-parcel path { --dl: 1560ms; }
  .js .dwg .cad--survey .cad-subd   path { --dl: 1680ms; }
  .js .dwg .cad--survey .cad-eutil  path { --dl: 1800ms; }
  .js .dwg .cad--survey .cad-esmt   path { --dl: 1880ms; }
  .js .dwg .cad--survey .cad-eelec  path { --dl: 1960ms; }
  .js .dwg .cad--survey .cad-esan   path { --dl: 2040ms; }
  .js .dwg .cad--survey .cad-cl     path { --dl: 2160ms; }
  .js .dwg .cad--survey .cad-align  path { --dl: 2320ms; }
  .js .dwg .cad--survey .cad-sheet  path { --dl: 2560ms; }
  .js .dwg .cad-seclab { opacity: 0; }

  /* Corner recovery. Each monument drops in on its own, in the order they are
     keyed, and only once all eleven are up do the lines between them run. That
     is the order the survey happened in. */
  .js .dwg .cad--survey .cad-mon__m,
  .js .dwg .cad-monlab text { opacity: 0; }
  .js .dwg.is-plotting .cad--survey .cad-mon__m {
    animation: mon-set 420ms cubic-bezier(.2,1.4,.5,1)
               calc(var(--i, 0) * 78ms) forwards;
  }
  .js .dwg.is-plotting .cad-monlab text {
    animation: mon-key 300ms ease calc(var(--i, 0) * 78ms + 240ms) forwards;
  }
  @keyframes mon-set {
    from { opacity: 0; transform: scale(2.6); }
    60%  { opacity: 1; }
    to   { opacity: 1; transform: scale(1); }
  }
  @keyframes mon-key { to { opacity: 1; } }
  /* Scale about the marker itself, not the corner of the drawing. */
  .cad--survey .cad-mon__m { transform-box: fill-box; transform-origin: center; }

  .js .dwg.is-plotting .cad-seclab {
    opacity: 1;
    transition: opacity 450ms ease 2100ms;
  }
  .js .dwg.is-plotted .cad-seclab { opacity: 1; }
  .js .dwg.is-plotted .cad--survey .cad-mon__m,
  .js .dwg.is-plotted .cad-monlab text { opacity: 1; }
  /* Drawn solid, then the centerline linetype snaps in. */
  .js .dwg.is-plotted .cad--survey .cad-align path { stroke-dasharray: 26 7 5 7; }

  /* On a phone the whole framework at 335px is a smudge, and the monument
     keys stop being legible long before the linework does. Give it a floor
     and pan, the same as the roadway sheet. */
  @media (max-width: 52rem) {
    .dwg--wide .cad--survey { min-inline-size: 900px; }
    .dwg--wide:has(.cad--survey) .dwg__frame::after {
      content: "Drag to pan the framework \2192";
    }
  }

  /* --- monument list -------------------------------------------------- */
  .mons {
    list-style: none;
    margin: 1rem 0 0;
    padding: .8rem 1rem;
    border: 1px solid color-mix(in oklab, currentColor 18%, transparent);
    border-radius: var(--r, 4px);
    font: 500 .76rem/1.6 var(--font-draft, ui-monospace, monospace);
    color: var(--fg-muted, #b9cbe2);
    columns: 2;
    column-gap: 2.2rem;
  }
  .mons li {
    break-inside: avoid;
    margin-block-end: .45rem;
  }
  .mons b {
    display: inline-block;
    min-inline-size: 1.6em;
    color: #ffd08a;
    font-weight: 700;
  }
  .mons i {
    display: block;
    padding-inline-start: 1.6em;
    font-style: normal;
    opacity: .78;
  }
  @media (max-width: 47.99rem) {
    .mons { columns: 1; font-size: .72rem; }
  }
  @media (prefers-reduced-motion: reduce), print {
    .js .dwg .cad-monlab text,
    .js .dwg .cad-seclab,
    .js .dwg .cad--survey .cad-mon__m { opacity: 1; animation: none; }
  }
}

@layer components {
  /* ---------------------------------------------------------------------
     M. THE PLAT, WALKED AS A TRAVERSE.

     A legal description is a list of courses: from each corner, a bearing and
     a distance to the next, ending back where it started. So the boundary is
     drawn that way rather than all at once, one course at a time, with the
     bearing and distance on screen as the marker walks it, and the closure at
     the end. The numbers are computed off the drawing in build_plat.py.
     --------------------------------------------------------------------- */
  .cad--plat .cad-lot    { stroke: #6f8298; stroke-width: .9; }
  .cad--plat .cad-parcel { stroke: #9db4d0; stroke-width: 1; }
  .cad--plat .cad-cl     { stroke: #7ea6d8; stroke-width: 1; }
  .cad--plat .cad-esmt   { stroke: #d9b64a; stroke-width: 1; }
  .cad--plat .cad-new    { stroke: #7cf0c4; stroke-width: 1.4; }
  .cad--plat .cad-bdy    { stroke: #ff9d4d; stroke-width: 2.4; }
  .cad--plat .cad-mon    { stroke: #ffd08a; stroke-width: 1.6; }

  .cad--plat .cad-t {
    stroke: none;
    text-anchor: start;
    dominant-baseline: hanging;
    font-family: var(--font-draft, ui-monospace, monospace);
  }
  /* Sized against the width the drawing is rendered at (viewBox is 2000), so
     a label is the same size on screen at 100% and at 800%. */
  .cad--plat .cad-t--blk { fill: #c3d4e6; font-weight: 600; letter-spacing: .1em;
                           font-size: calc(15px * 2000 / var(--dw, 736)); }
  .cad--plat .cad-t--new { fill: #7cf0c4; font-weight: 600;
                           font-size: calc(11px * 2000 / var(--dw, 736)); }
  .cad--plat .cad-t--st  { fill: #93a9c0; font-size: calc(9px * 2000 / var(--dw, 736)); }
  .cad--plat .cad-t--brg { fill: #d9b64a; font-size: calc(9px * 2000 / var(--dw, 736)); }
  .cad--plat .cad-t--num { fill: #66798f; font-size: calc(7px * 2000 / var(--dw, 736)); }
  /* Reduced, a sheet drops its fine lettering rather than printing it unreadable. */
  [data-detail="low"] .cad--plat .cad-t--num,
  [data-detail="low"] .cad--plat .cad-t__x { display: none; }

  .dwg__layers li[data-g="lot"].is-on    { color: #8ea4bc; }
  .dwg__layers li[data-g="parcel"].is-on { color: #9db4d0; }
  .dwg__layers li[data-g="new"].is-on    { color: #7cf0c4; }
  .dwg__layers li[data-g="bdy"].is-on    { color: #ff9d4d; }
  .dwg__layers li[data-g="t"].is-on      { color: #c3d4e6; }

  /* --- the walk -------------------------------------------------------- */
  .js .dwg .cad--plat .cad-lot    path { --dl: 0ms; }
  .js .dwg .cad--plat .cad-parcel path { --dl: 220ms; }
  .js .dwg .cad--plat .cad-cl     path { --dl: 380ms; }
  .js .dwg .cad--plat .cad-esmt   path { --dl: 480ms; }
  .js .dwg .cad--plat .cad-new    path { --dl: 3600ms; }
  /* One course at a time. TRAV_START and TRAV_PER in fx.js match these. */
  .js .dwg.is-plotting .cad--plat .cad-bdy path {
    animation: dwg-draw 600ms linear calc(1100ms + var(--i, 0) * 600ms) forwards;
  }
  .js .dwg .cad--plat .cad-mon,
  .js .dwg .cad--plat .cad-t { opacity: 0; }
  .js .dwg.is-plotting .cad--plat .cad-mon {
    opacity: 1;
    transition: opacity 400ms ease 3450ms;
  }
  .js .dwg.is-plotting .cad--plat .cad-t {
    opacity: 1;
    transition: opacity 500ms ease 3800ms;
  }
  .js .dwg.is-plotted .cad--plat .cad-mon,
  .js .dwg.is-plotted .cad--plat .cad-t { opacity: 1; }
  /* The plat has its own leading edge; the plotter bar would fight it. */
  .dwg--plat .dwg__bar { display: none; }

  /* --- the marker walking the boundary --------------------------------- */
  .trav__dot {
    position: absolute;
    inline-size: 13px;
    block-size: 13px;
    margin: -7px 0 0 -7px;
    border-radius: 50%;
    background: #ffd08a;
    box-shadow: 0 0 0 2px rgba(11, 22, 38, .9), 0 0 14px 3px rgba(255, 157, 77, .5);
    pointer-events: none;
    z-index: 4;
  }
  .trav__dot[hidden] { display: none; }

  .trav {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .3em 1em;
    margin-block-start: .75rem;
    font: 500 .82rem/1.5 var(--font-draft, ui-monospace, monospace);
    color: var(--fg-muted, #b9cbe2);
  }
  .trav__k {
    font-size: .74em;
    letter-spacing: .14em;
    text-transform: uppercase;
    opacity: .82;
  }
  .trav b {
    color: var(--fg, #fff);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    letter-spacing: .02em;
  }
  /* A drawing reduced is a drawing lettered bigger, with the fine detail
     dropped. Same thing here: rather than force a 700px pannable box onto a
     phone, the plat keeps fitting and its lettering scales up. The original
     lot numbers go, because at this size they are 44 illegible marks. */
  @media (max-width: 47.99rem) {
    .trav { font-size: .74rem; }
    .cad--plat .cad-lot { stroke-width: 1.1; }
  }
  @media (prefers-reduced-motion: reduce), print {
    .js .dwg .cad--plat .cad-mon,
    .js .dwg .cad--plat .cad-t { opacity: 1; }
    .js .dwg.is-plotting .cad--plat .cad-bdy path { animation: none; }
    .trav__dot { display: none; }
  }
}

@layer components {
  /* ---------------------------------------------------------------------
     N. ZOOM, IN PLACE. These are working drawings and they are meant to be
     read closely, so they magnify where they sit rather than taking over the
     page.

     Zooming widens the drawing inside its own frame and lets the frame scroll,
     rather than transforming it. That keeps native scrolling, momentum and
     scrollbars, keeps the keyboard working, and keeps every percentage-placed
     marker on the drawing (the traverse dot, the station cursor) correct
     without a line of extra maths.
     --------------------------------------------------------------------- */
  .zoombox { position: relative; }
  .zoombox > .dwg__frame,
  .zoombox > .sheet__scroll {
    overflow: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
    scrollbar-color: color-mix(in oklab, white 28%, transparent) transparent;
  }
  .zoomer { position: relative; }
  .is-zoomed > .dwg__frame,
  .is-zoomed > .sheet__scroll { cursor: grab; }
  .is-zoomed > .dwg__frame:active,
  .is-zoomed > .sheet__scroll:active { cursor: grabbing; }

  .zoomctl {
    position: absolute;
    inset-block-end: 10px;
    inset-inline-end: 10px;
    z-index: 5;
    display: flex;
    align-items: stretch;
    gap: 1px;
    padding: 1px;
    border-radius: 999px;
    background: color-mix(in oklab, #06101c 82%, transparent);
    border: 1px solid rgba(255, 255, 255, .18);
    backdrop-filter: blur(6px);
    font-family: var(--font-draft, ui-monospace, monospace);
    opacity: .55;
    transition: opacity 160ms ease;
  }
  .zoombox:hover .zoomctl,
  .zoombox:focus-within .zoomctl,
  .zoombox.is-zoomed .zoomctl { opacity: 1; }
  .zoomctl__b {
    font: inherit;
    font-size: 15px;
    line-height: 1;
    min-inline-size: 30px;
    min-block-size: 30px;
    display: grid;
    place-items: center;
    color: #dce6f2;
    background: none;
    border: 0;
    border-radius: 999px;
    cursor: pointer;
    padding: 0;
  }
  .zoomctl__b:hover { background: rgba(255, 255, 255, .14); }
  .zoomctl__b:focus-visible { outline: 2px solid #fff; outline-offset: -2px; }
  .zoomctl__b[disabled] { opacity: .35; cursor: default; }
  .zoomctl__b[disabled]:hover { background: none; }
  .zoomctl__lvl {
    display: grid;
    place-items: center;
    min-inline-size: 3.6em;
    padding-inline: .3em;
    font-size: 11px;
    letter-spacing: .04em;
    font-variant-numeric: tabular-nums;
    color: #9fb3c9;
  }
  @media (pointer: coarse) {
    .zoomctl__b { min-inline-size: 40px; min-block-size: 40px; font-size: 17px; }
  }

  /* Said once, on the first wheel over a drawing that has not been zoomed. */
  .zoomhint {
    position: absolute;
    inset-block-end: 10px;
    inset-inline-start: 10px;
    z-index: 5;
    padding: 6px 10px;
    border-radius: 999px;
    background: color-mix(in oklab, #06101c 86%, transparent);
    border: 1px solid rgba(255, 255, 255, .16);
    font: 500 11px/1 var(--font-draft, ui-monospace, monospace);
    letter-spacing: .07em;
    text-transform: uppercase;
    color: #cfdcea;
    pointer-events: none;
    opacity: 0;
    transition: opacity 220ms ease;
  }
  .zoomhint.is-on { opacity: 1; }

  @media print {
    .zoomctl, .zoomhint { display: none; }
  }
}

@layer components {
  /* A field arrived already answered from the page you came from. Say so for a
     moment, then get out of the way. */
  .is-prefilled {
    animation: prefill-flash 2.4s ease;
  }
  @keyframes prefill-flash {
    0%, 70% { box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent, #E87722) 45%, transparent); }
    100%    { box-shadow: 0 0 0 3px transparent; }
  }
  @media (prefers-reduced-motion: reduce) { .is-prefilled { animation: none; } }
}
