/* Simulation studio.
   House style: the DeepEarth Simulator's. White ground, black ink, Oxygen for
   structure, Cormorant Garamond for anything that should read as drawn rather
   than typed. Hairlines, not boxes. Measured contrast in the design skill
   (.claude/skills/simulation-studio-design). */

*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
/* The `hidden` ATTRIBUTE only sets display:none at user-agent specificity, so
   any class rule with its own `display` silently defeats it. That is why the
   site-meta strip and the map tools were showing empty placeholders before a
   site existed: `.site-meta{display:flex}` outranked `[hidden]`. */
[hidden]{display:none!important}
:root{
    --ink:#111;                /* 18.9:1 on white */
    --dim:#4d4d4d;             /* 8.45:1 */
    --faint:#737373;           /* 4.74:1, the floor for anything read */
    --line:rgba(0,0,0,.12);
    --line-strong:rgba(0,0,0,.38);
    --bg:#fff;
    --panel:#fafafa;
    --accent:#111;             /* ink: selected and interactive, drawn by weight, not hue */
    --sel:rgba(0,0,0,.035);    /* the ground under a selected card */
    --warn:#9c3d1a;            /* 6.6:1; the one warm tone, for what needs attention */
    --bar-h:58px;
    --rail-w:376px;
}
html,body{height:100%}
body{
    background:var(--bg);color:var(--ink);
    font-family:'Oxygen',sans-serif;font-weight:300;
    -webkit-font-smoothing:antialiased;
    font-size:14px;line-height:1.6;
}
.mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:.82em}
.per{color:var(--faint);font-size:.78em;letter-spacing:.04em}

/* ─────────────────────────── top bar ─────────────────────────── */
.bar{
    position:fixed;top:0;left:0;right:0;height:var(--bar-h);z-index:60;
    display:flex;align-items:center;justify-content:space-between;
    padding:0 22px;background:rgba(255,255,255,.9);
    backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);
    border-bottom:1px solid var(--line);
}
.bar-l,.bar-r{display:flex;align-items:center;gap:14px}
.bar-mark{height:26px;display:block}   /* the viewer sets its lockup at 30 px; below ~24 the wordmark stops reading */
.bar-l a{display:flex;align-items:center;text-decoration:none}
.bar-sep{width:1px;height:16px;background:var(--line-strong)}
/* The product's name as the viewer sets it: DeepEarth in weight, Simulator in italic. */
.bar-title{font-family:'Cormorant Garamond',Georgia,serif;font-weight:300;font-size:1.12rem;
    letter-spacing:.005em;color:var(--ink);white-space:nowrap}
.bar-title b{font-weight:600}
.bar-title i{font-style:italic;font-weight:300}
/* The name is the way to the account page, so it is a link and has to look
   like one on hover. It keeps its unadorned type: underlining it would put a
   third link style in a bar that already has two. */
.bar-user{font-family:'Cormorant Garamond',serif;font-style:italic;
    font-size:1.02rem;color:var(--dim);text-decoration:none;
    transition:color .25s ease}
.bar-user:hover{color:var(--accent)}
.bar-out{font-size:.54rem;letter-spacing:.28em;text-indent:.28em;
    text-transform:uppercase;color:var(--faint);text-decoration:none;
    transition:color .25s ease}
.bar-out:hover{color:var(--ink)}
/* Navigation in the bar. Bordered so it reads as somewhere to GO, where
   "Log out" beside it stays an unboxed text action: two links of equal weight
   next to each other would make leaving look like a destination. */
.bar-nav{font-size:.54rem;letter-spacing:.28em;text-indent:.28em;
    text-transform:uppercase;color:var(--dim);text-decoration:none;
    padding:.36rem .8rem;border:1px solid var(--line-strong);white-space:nowrap;
    transition:color .25s ease,border-color .25s ease}
.bar-nav:hover{color:var(--accent);border-color:var(--accent)}
.chip{font-size:.5rem;letter-spacing:.24em;text-indent:.24em;text-transform:uppercase;
    padding:.26rem .6rem;border:1px solid var(--line-strong);color:var(--dim)}
.chip-pro{border-color:var(--accent);color:var(--accent)}

/* ─────────────────────────── layout ─────────────────────────── */
.shell{display:flex;padding-top:var(--bar-h);min-height:100%}
.work{flex:1;min-width:0;padding:52px 46px 120px;max-width:1080px}
.rail{
    width:var(--rail-w);flex:0 0 var(--rail-w);
    border-left:1px solid var(--line);background:var(--panel);
}
.rail-inner{position:sticky;top:var(--bar-h);padding:34px 30px 40px}

/* ─────────────────────────── steps ─────────────────────────── */
.step{margin-bottom:74px;transition:opacity .5s ease}
/* Unselectable, not unreadable. On white at .6, --ink still reads at 4.8:1 and
   --dim at 2.9:1 (the design skill carries the table). With no hue left in the
   palette the separation is the opacity plus the missing affordances: a locked
   step takes no pointer, and nothing in it carries an ink border. The
   saturate(0) stays for the map and any imagery inside a locked step. */
.step.is-locked{opacity:.6;pointer-events:none;filter:saturate(0)}
.step-head{margin-bottom:26px}
.step-n{
    display:block;font-size:.54rem;letter-spacing:.4em;
    color:var(--faint);margin-bottom:.7rem;
}
.step h2{
    font-family:'Cormorant Garamond',serif;font-weight:300;
    font-size:2.05rem;line-height:1.1;letter-spacing:-.005em;
}
.step-sub{color:var(--dim);font-size:.92rem;margin-top:.5rem;max-width:62ch}

/* ─────────────────────── 01 site input ─────────────────────── */
.site-input{display:flex;align-items:stretch;gap:20px;margin-bottom:22px}
.addr{flex:1;display:flex;gap:10px;align-items:flex-end}
.addr input{
    flex:1;background:transparent;border:0;border-bottom:1px solid var(--line-strong);
    color:var(--ink);font-family:'Cormorant Garamond',serif;font-style:italic;
    font-size:1.28rem;padding:.4rem .1rem;outline:none;
    transition:border-color .3s ease;-webkit-appearance:none;border-radius:0;
}
.addr input::placeholder{color:var(--faint)}
.addr input:focus{border-bottom-color:var(--ink)}
.or{align-self:center;font-size:.54rem;letter-spacing:.3em;text-transform:uppercase;
    color:var(--faint);padding:0 4px}
.drop{
    flex:0 0 262px;border:1px dashed var(--line-strong);
    display:flex;flex-direction:column;align-items:center;justify-content:center;
    padding:.9rem;cursor:pointer;text-align:center;
    transition:border-color .25s ease,background .25s ease;
}
.drop:hover,.drop.is-over{border-color:var(--accent);background:var(--sel)}
.drop-main{font-size:.84rem;color:var(--dim)}
.drop-main b{font-weight:400;color:var(--ink)}
.drop-sub{font-size:.62rem;letter-spacing:.14em;text-transform:uppercase;
    color:var(--faint);margin-top:.3rem}

/* ─────────────────────────── map ─────────────────────────── */
.map-wrap{position:relative;height:420px;border:1px solid var(--line);overflow:hidden}
#map{position:absolute;inset:0;background:#f2f2f2}
.map-hint{
    position:absolute;inset:0;z-index:500;display:flex;
    align-items:center;justify-content:center;pointer-events:none;
    font-family:'Cormorant Garamond',serif;font-style:italic;
    font-size:1.1rem;color:var(--dim);
    background:rgba(255,255,255,.7);transition:opacity .5s ease;
}
.map-hint.is-gone{opacity:0}
.map-hint.is-tip{inset:auto 0 0 0;padding:.55rem 1rem 1.6rem;font-size:1rem;text-align:center;background:rgba(255,255,255,.88)}
/* Edit mode puts up to three controls here at once. They wrap rather than run
   off the map, and the rail keeps clear of the left edge so a wrapped row
   cannot cover the zoom control. */
.map-tools{position:absolute;right:12px;top:12px;z-index:600;display:flex;gap:8px;
    flex-wrap:wrap;justify-content:flex-end;max-width:calc(100% - 68px)}
/* While a polygon is being drawn the map is a drawing surface: the crosshair is
   set on the container, and text selection on a drag would otherwise highlight
   the attribution and the zoom control instead of drawing. */
.map-wrap.is-drawing .leaflet-container{cursor:crosshair;user-select:none;-webkit-user-select:none}
.tool{
    background:rgba(255,255,255,.92);border:1px solid var(--line-strong);color:var(--dim);
    font-family:'Oxygen',sans-serif;font-size:.54rem;letter-spacing:.2em;
    text-transform:uppercase;padding:.5rem .8rem;cursor:pointer;
    transition:color .25s ease,border-color .25s ease;
}
.tool:hover,.tool.is-on{color:var(--ink);border-color:var(--ink)}
/* Close polygon before the third corner: real, but not yet. The unselectable
   state of the three-state grammar, at the disabled button's .5. */
.tool:disabled{opacity:.5;pointer-events:none;cursor:default}
.leaflet-container{background:#f2f2f2;font-family:'Oxygen',sans-serif}
.leaflet-control-attribution{
    background:rgba(255,255,255,.82)!important;color:var(--faint)!important;font-size:9px!important}
.leaflet-control-attribution a{color:var(--dim)!important}
.leaflet-bar{border:1px solid var(--line-strong)!important;border-radius:0!important;box-shadow:none!important}
.leaflet-bar a{background:#fff!important;color:var(--dim)!important;border-color:var(--line)!important;border-radius:0!important}
.leaflet-bar a:hover{background:#f4f4f4!important;color:var(--ink)!important}

/* ───────────────────────── site meta ───────────────────────── */
.site-meta{display:flex;gap:0;margin-top:18px;border:1px solid var(--line)}
.meta-cell{flex:1;padding:.85rem 1.1rem;border-right:1px solid var(--line);min-width:0}
.meta-cell:last-child{border-right:0}
.meta-k{display:block;font-size:.52rem;letter-spacing:.28em;text-transform:uppercase;
    color:var(--faint);margin-bottom:.35rem}
.meta-v{display:block;font-size:.92rem;color:var(--ink);
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* ─────────────── latest aerial survey, under the site ─────────────── */
/* Hung from the site strip, sharing its border, so the two read as one instrument:
   where the site is, then what was last flown over it. */
.capture{border:1px solid var(--line);border-top:0}
.cap-h{font-size:.52rem;letter-spacing:.28em;text-indent:.28em;text-transform:uppercase;
    color:var(--faint);padding:.8rem 1.1rem 0}
.cap-row{display:grid;grid-template-columns:repeat(4,1fr)}
.cap-row .meta-cell{border-right:1px solid var(--line);padding-top:.55rem}
.cap-row .meta-cell:last-child{border-right:0}
.capture.is-loading .meta-v{color:var(--faint)}
.cap-note{padding:0 1.1rem .85rem;font-family:'Cormorant Garamond',Georgia,serif;font-style:italic;
    font-size:.98rem;line-height:1.4;color:var(--dim)}
.cap-note:empty{display:none}
@media(max-width:600px){
    .cap-row{grid-template-columns:repeat(2,1fr)}
    .cap-row .meta-cell:nth-child(2){border-right:0}
    .cap-row .meta-cell:nth-child(-n+2){border-bottom:1px solid var(--line)}
}

/* ───────────────────── signed out, said once ───────────────────── */
/* Over everything, because every control behind it has stopped working. It does
   not reload the page: the work on screen (hand-drawn bounds, a typed name, a
   mounted payment form) is still worth something and a reload would take it. */
.ei-ended{position:fixed;inset:0;z-index:200;display:flex;align-items:center;
    justify-content:center;padding:24px;background:rgba(255,255,255,.82);
    -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px)}
.ei-ended-box{max-width:460px;background:var(--bg);border:1px solid var(--line-strong);
    padding:24px 26px 20px}
.ei-ended-box b{display:block;font-family:'Cormorant Garamond',Georgia,serif;
    font-weight:300;font-size:1.5rem;line-height:1.15;margin-bottom:10px}
.ei-ended-box p{font-size:.82rem;color:var(--dim);line-height:1.6}
.ei-ended-actions{display:flex;gap:10px;margin-top:20px;align-items:center;flex-wrap:wrap}
.ei-ended-actions .btn{text-decoration:none}
.ei-ended-actions .btn-solid{width:auto;padding:.62rem 1.3rem}

/* ─────────────────── 02 the single product on sale ─────────────────── */
/* One thing to buy, so it is a statement and not a card in a row of cards: full
   width, no hover, no selected state, nothing to click. It keeps the tier card's
   facts (rate, floor, precision, what it is built from, the live coverage line,
   which reuses .tier-curr unchanged) because those were never about choosing. */
.solo{border:1px solid var(--line);padding:22px 24px 18px}
.solo-top{display:flex;align-items:baseline;justify-content:space-between;gap:14px;flex-wrap:wrap}
.solo-name{font-family:'Cormorant Garamond',serif;font-size:1.72rem;font-weight:400}
.solo-price{display:flex;align-items:baseline;gap:.5rem;flex-wrap:wrap}
.solo-price b{font-weight:400;font-size:1.18rem}
.solo-min{font-size:.8rem;color:var(--dim)}
.solo-spec{display:flex;align-items:baseline;gap:1.1rem;flex-wrap:wrap;margin-top:.7rem}
.solo-res{font-size:.56rem;letter-spacing:.24em;text-transform:uppercase;color:var(--dim)}
.solo-src{font-size:.85rem;color:var(--dim)}
/* Cannot be delivered at this site (deliverable.py). With no sibling card to be
   chosen instead, going grey would say nothing, so the reason is a sentence and
   Continue stays off from the same verdict. */
.solo.is-unavailable{opacity:.72;filter:saturate(0)}
.confirm-first{font-size:.8rem;line-height:1.55;color:var(--dim);margin:.9rem 0 0}
.confirm-first .link{font-size:inherit;padding:0;margin-left:.25rem;text-decoration:underline;text-underline-offset:2px}
.solo-why{font-size:.85rem;line-height:1.55;color:var(--warn);margin-top:.9rem;
    padding-top:.8rem;border-top:1px solid var(--line)}
/* Only for `unknown`: a lookup that failed is retryable, where a site with no
   survey over it is not, and offering "check again" for the second would be a
   button that can only ever disappoint. */
.solo-retry{font:inherit;font-size:.54rem;letter-spacing:.24em;text-indent:.24em;
    text-transform:uppercase;color:var(--ink);background:transparent;
    border:1px solid var(--line-strong);padding:.3rem .7rem;margin-left:.4rem;
    cursor:pointer;white-space:nowrap;transition:border-color .25s ease}
.solo-retry:hover:not(:disabled){border-color:var(--ink)}
.solo-retry:disabled{opacity:.5;cursor:not-allowed}
/* The same "Check again" on a tier card that could not be confirmed: the card ignores pointers while unavailable,
   so its reason line takes them back for the button. */
.tier-retry{font:inherit;font-size:.54rem;letter-spacing:.24em;text-indent:.24em;
    text-transform:uppercase;color:var(--ink);background:transparent;
    border:1px solid var(--line-strong);padding:.3rem .7rem;margin-left:.4rem;
    cursor:pointer;white-space:nowrap;transition:border-color .25s ease}
.tier-retry:hover:not(:disabled){border-color:var(--ink)}
.tier-retry:disabled{opacity:.5;cursor:not-allowed}
/* is-unavailable desaturates the block; the retry must not read as disabled too. */
.solo.is-unavailable{pointer-events:auto}

/* ─────────────────────── 02 service tiers ─────────────────────── */
.tiers{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}
.tiers-2{grid-template-columns:repeat(2,1fr);margin:.4rem 0 1rem}
.tier-word{font-size:.62em;letter-spacing:.14em;text-transform:uppercase;color:var(--faint,#999);font-family:Oxygen,sans-serif}
@media(max-width:720px){.tiers-2{grid-template-columns:1fr}}
.tier{
    border:1px solid var(--line);padding:22px 20px 18px;cursor:pointer;
    display:flex;flex-direction:column;
    transition:border-color .28s ease,background .28s ease,transform .28s ease;
}
.tier:hover{border-color:var(--line-strong);background:var(--sel)}
/* Selected is drawn by weight: the hairline doubles to ink. No fill, no hue. */
.tier.is-on{border-color:var(--ink);box-shadow:inset 0 0 0 1px var(--ink);background:#fff}
/* Cannot be delivered at this site (deliverable.py): the unselectable state of the
   three-state grammar, with the reason kept readable under it. */
/* Pointers still reach it, so its Check again button works; the tier click handler refuses the card itself. */
.tier.is-unavailable{opacity:.62;filter:saturate(0);cursor:default}
.tier-why{font-size:.8rem;line-height:1.5;color:var(--ink);margin-top:.8rem;
    padding-top:.7rem;border-top:1px solid var(--line)}
.tier-top{display:flex;align-items:baseline;justify-content:space-between;gap:8px}
.tier-name{font-family:'Cormorant Garamond',serif;font-size:1.5rem;font-weight:400}
.tier-price b{font-weight:400;font-size:1.02rem}
.tier-res{font-size:.56rem;letter-spacing:.24em;text-transform:uppercase;
    color:var(--dim);margin:.55rem 0 .9rem}
/* A service's minimum sits under its precision, in the body face, so it reads
 * as a fact about the price before the site is drawn, not as a warning after. */
.tier-min{font-size:.8rem;color:var(--ink);margin:-.4rem 0 .9rem}
.tier-pts{list-style:none;flex:1}
.tier-pts li{
    font-size:.85rem;color:var(--dim);padding-left:1rem;position:relative;
    margin-bottom:.3rem;line-height:1.5;
}
.tier-pts li::before{content:'';position:absolute;left:0;top:.62em;
    width:5px;height:1px;background:var(--faint)}
.tier-curr{
    margin-top:1rem;padding-top:.8rem;border-top:1px solid var(--line);
    font-size:.74rem;color:var(--dim);display:flex;align-items:center;gap:.5rem;
    min-height:2.2rem;flex-wrap:wrap;
}
/* A line with nothing to say is not drawn: a lone dot under a card's list read as a defect. */
.tier-curr:has(> .txt:empty):not(:has(.acc)){display:none}
/* The measured accuracy line sits UNDER the currency line rather than beside
   it: `flex:0 0 100%` forces its own row at every width, so it does not
   compete with the date on a phone. Indented past the dot so the two read as
   one block. Only ever rendered when the sweep actually measured this work
   unit against NGS monuments, so an empty row is not possible. */
/* `--faint` rather than an opacity on top of `--dim`. Stacking opacity is what
   put body copy at 1.2:1 on this page once already: .70 dimmed by .72 lands
   near 4.4:1, under the floor, and then a locked step multiplies it again.
   The token is 4.92:1 live and 2.44:1 inside a locked step, which is the
   measured unselectable treatment rather than an accident. No accent here on
   purpose: accent means selected or interactive, and this is neither. */
.tier-curr .acc,
.tier-curr .dens{
    flex:0 0 100%;padding-left:1rem;font-size:.70rem;
    color:var(--faint);letter-spacing:.01em;
}
/* A public LiDAR service down (server.lidar_outage): the same row, in the warning ink, read before payment. */
.tier-curr .outage{flex:0 0 100%;padding-left:1rem;font-size:.70rem;color:var(--warn);letter-spacing:.01em}
.tier-curr .dot{width:5px;height:5px;border-radius:50%;background:var(--faint);flex:0 0 5px}
.tier-curr.ok .dot{background:var(--accent)}
.tier-curr.none .dot{background:var(--warn)}
/* Data is current AND readable, but no delivery has run on this format. Keeps
   the accent dot, since the answer is still yes, and hangs the qualifier off
   the text rather than turning the row into a warning. */
.tier-curr.caution .dot{background:var(--accent)}
.tier-curr.caution .txt{opacity:.72}

/* ───────────── 02b included with all tiers ───────────── */
/* The manifest lives with the tiers, because "what does the price buy" is
   asked while the price is being chosen. One column, hairline-separated rows,
   no boxes and no controls: nothing here is a choice. */
.incl-all{border:1px solid var(--line);margin-top:26px}
.incl-h{
    font-size:.54rem;letter-spacing:.34em;text-indent:.34em;text-transform:uppercase;
    color:var(--faint);padding:15px 24px;border-bottom:1px solid var(--line);
}
.incl-list{padding:0 24px}
.incl-list .inc{padding:17px 0;margin-bottom:0;border-top:1px solid var(--line)}
.incl-list .inc:first-child{border-top:0}
/* Full page width, so the items get two tracks rather than a tall thin list. */
.incl-list .inc-items{columns:2;column-gap:34px}

/* ─────────────────────── 03 add-ons ─────────────────────── */
/* An offer and a roadmap. Full-height hairline between them rather than a gap,
   matching .site-meta, so the two read as one instrument. */
.targets{
    display:grid;grid-template-columns:1.75fr 1fr;
    border:1px solid var(--line);
}
.tcol{padding:22px 26px 26px;border-right:1px solid var(--line);min-width:0}
.tcol:last-child{border-right:0}
.tcol-h{
    font-size:.54rem;letter-spacing:.34em;text-indent:.34em;text-transform:uppercase;
    color:var(--faint);padding-bottom:.9rem;margin-bottom:1.35rem;
    border-bottom:1px solid var(--line);
}
/* The two offers sit side by side, echoing the rhythm of the tier cards above
   rather than stretching one short blurb across the whole column. */
.ao-pair{display:grid;grid-template-columns:1fr 1fr;gap:12px;align-items:start}

/* included, and coming soon: same type, different weight of presence */
.inc{margin-bottom:1.7rem}
.inc:last-child{margin-bottom:0}
.inc.is-soon{opacity:.68;filter:saturate(0)}
.inc-head{display:flex;align-items:baseline;gap:12px}
.inc-name{font-family:'Cormorant Garamond',serif;font-size:1.22rem;flex:1;
    line-height:1.25}
.inc-items{list-style:none;margin:.55rem 0 0}
.inc-items > li{font-size:.84rem;color:var(--dim);margin-bottom:.26rem;
    break-inside:avoid;position:relative;padding-left:.9rem}
.inc-items > li::before{content:'';position:absolute;left:0;top:.62em;
    width:4px;height:1px;background:var(--faint)}
.item-note{display:block;font-size:.74rem;color:var(--faint);
    font-style:italic;line-height:1.45;margin-top:.15rem}
.sub-items{list-style:none;margin:.2rem 0 .2rem .3rem}
.sub-items li{font-size:.8rem;color:var(--faint);padding-left:.9rem;position:relative}
.sub-items li::before{content:'';position:absolute;left:0;top:.62em;
    width:3px;height:1px;background:var(--faint)}

.target-note{font-size:.62rem;letter-spacing:.14em;text-transform:uppercase;
    color:var(--faint);text-align:right}
.note-pro{color:var(--accent)}
/* At 390px the tag took half the row and "Plant palettes and traits" broke over three lines; it goes under the name. */
@media(max-width:600px){.inc-head{flex-direction:column;gap:.15rem}.inc-head .target-note{text-align:left}}
.res-badge{
    display:inline-block;border:1px solid var(--line-strong);color:var(--dim);
    padding:.1rem .4rem;margin-right:.5rem;letter-spacing:.1em;
}

/* the only interactive things left in this section */
.tick{
    width:15px;height:15px;flex:0 0 15px;border:1px solid var(--line-strong);
    position:relative;transition:border-color .2s ease,background .2s ease;
}
.tick::after{
    content:'';position:absolute;inset:3px;background:var(--accent);
    opacity:0;transition:opacity .2s ease;
}
input:checked + .tick{border-color:var(--accent)}
input:checked + .tick::after{opacity:1}

.ao{
    display:block;border:1px solid var(--line);padding:15px 16px 16px;
    cursor:pointer;
    transition:border-color .25s ease,background .25s ease;
}
.ao:hover{border-color:var(--line-strong)}
.ao.is-on{border-color:var(--ink);box-shadow:inset 0 0 0 1px var(--ink)}
.ao-for{
    display:block;font-size:.5rem;letter-spacing:.22em;text-transform:uppercase;
    color:var(--faint);margin-bottom:.7rem;
}
.ao-top{display:flex;align-items:flex-start;gap:10px}
.ao-name{font-family:'Cormorant Garamond',serif;font-size:1.08rem;line-height:1.25}
.ao-price{margin-top:.65rem;font-size:.95rem;white-space:nowrap}
.ao-blurb{font-size:.76rem;color:var(--faint);font-style:italic;
    line-height:1.45;margin-top:.4rem}

/* ─────────────────────────── rail ─────────────────────────── */
.rail-head{font-size:.54rem;letter-spacing:.42em;text-indent:.42em;
    text-transform:uppercase;color:var(--faint);
    padding-bottom:1rem;border-bottom:1px solid var(--line)}
.rail-site{padding:1.2rem 0;border-bottom:1px solid var(--line)}
.rs-label{font-family:'Cormorant Garamond',serif;font-style:italic;
    font-size:1.02rem;color:var(--dim);line-height:1.4;
    display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.rs-acres{font-size:1.6rem;margin-top:.4rem;font-weight:300}
.rail-lines{padding:1.1rem 0;border-bottom:1px solid var(--line);min-height:96px}
.rl-empty{color:var(--faint);font-style:italic;
    font-family:'Cormorant Garamond',serif;font-size:.96rem}
.rl{display:flex;justify-content:space-between;gap:1rem;margin-bottom:.85rem}
.rl-l{min-width:0}
.rl-name{font-size:.9rem}
.rl-detail{font-size:.72rem;color:var(--faint);margin-top:.1rem}
.rl-c{font-size:.92rem;white-space:nowrap}
.rail-total{display:flex;justify-content:space-between;align-items:baseline;
    padding:1.2rem 0;font-size:.6rem;letter-spacing:.3em;text-transform:uppercase;
    color:var(--faint)}
.rt-num{font-family:'Cormorant Garamond',serif;font-size:2.1rem;
    letter-spacing:0;text-transform:none;color:var(--ink)}
.rail-trial{
    border:1px solid var(--accent);padding:.9rem 1rem;margin-bottom:1.2rem;
    font-size:.8rem;color:var(--dim);line-height:1.55;
}
.rail-trial b{color:var(--accent);font-weight:400;display:block;margin-bottom:.25rem}
/* On checkout the box is the headline alone, with no explanation under it, so
   the gap that separated the two would otherwise sit the text high in the box. */
.rail-trial b:only-child{margin-bottom:0}

/* ─────────────────────────── buttons ─────────────────────────── */
.btn{
    font-family:'Oxygen',sans-serif;font-size:.58rem;font-weight:400;
    letter-spacing:.3em;text-indent:.3em;text-transform:uppercase;
    padding:.9rem 1.6rem;cursor:pointer;border:1px solid var(--line-strong);
    background:transparent;color:var(--dim);
    transition:border-color .28s ease,color .28s ease,background .28s ease;
}
.btn:hover:not(:disabled){border-color:var(--ink);color:var(--ink)}
.btn-ghost{padding:.62rem 1.1rem}
.btn-solid{width:100%;border-color:var(--ink);color:var(--ink)}
.btn-solid:hover:not(:disabled){background:var(--ink);color:#fff}
.btn:disabled{opacity:.5;cursor:not-allowed}

/* ─────────────────────────── responsive ─────────────────────────── */
@media(max-width:1080px){
    .shell{flex-direction:column}
    .rail{width:100%;flex:1 1 auto;border-left:0;border-top:1px solid var(--line)}
    .rail-inner{position:static}
    .work{padding:38px 24px 40px;max-width:none}
    .tiers{grid-template-columns:1fr}
    .site-input{flex-direction:column}
    .drop{flex:1 1 auto}
    /* Stacked, the vertical rules between columns become horizontal ones. */
    .targets{grid-template-columns:1fr}
    .tcol{border-right:0;border-bottom:1px solid var(--line)}
    .tcol:last-child{border-bottom:0}
    .ao-pair{grid-template-columns:1fr}
    .incl-list .inc-items{columns:1}
}
@media(max-width:600px){
    .bar{padding:0 14px}
    .bar-mark{height:20px}
    /* The name used to be hidden here, which was free when it was decoration.
       It is the only route to the account page now, so it stays and is capped
       instead; a long name would otherwise push "Log out" off the edge. What
       gives way in its place is the SECOND nav button, marked .bar-nav-2 on
       each page: every page keeps exactly one, so nothing becomes unreachable
       and the mobile bar carries the same number of boxes it always did. */
    .bar-user{font-size:.9rem;max-width:9ch;overflow:hidden;
        text-overflow:ellipsis;white-space:nowrap}
    .bar-nav-2{display:none}
    /* MEASURED at 380px and 360px: with the name back, a subscriber's bar ran
       52px past the right edge and took "Log out" with it. The chip is the item
       to drop, because it is the only one here that is purely a statement: it
       says the account has the subscription, which the account page the name
       now reaches says in words, and which the "Browse traits" button hidden
       just above it already said. */
    .chip{display:none}
    /* The tracking comes down too: a heavily letter-spaced label is the first
       thing to overflow a narrow bar. */
    .bar-nav{padding:.3rem .55rem;letter-spacing:.16em;text-indent:.16em}
    /* MEASURED at 380px: adding the nav button overflowed the bar, and with the
       Pro chip present it ran clean off the right edge. The word "Simulation"
       at .42em tracking is the widest thing in here and it says what the mark
       immediately to its left already says, so it is the right thing to drop.
       `:not(.bar-back)` spares the LiDAR pages, where the same class is the
       link back to the list and hiding it would strand the viewer. */
    .bar-title:not(.bar-back){display:none}
    .bar-l .bar-sep{display:none}
    .bar-l,.bar-r{gap:10px}
    /* Without this "Log out" breaks across two lines and clips. */
    .bar-out{white-space:nowrap}
    .site-meta{flex-direction:column}
    .meta-cell{border-right:0;border-bottom:1px solid var(--line)}
    .step h2{font-size:1.7rem}
}

/* ─────────────────── map as a second drop target ─────────────────── */
/* The overlay is always in the DOM at opacity 0 rather than toggled with the
   `hidden` attribute: it must never take a paint or a layout step on the
   dragover path, and being inert means it cannot fire the dragleave that would
   otherwise strobe the highlight. */
.map-drop{
    position:absolute;inset:0;z-index:700;display:flex;
    align-items:center;justify-content:center;pointer-events:none;
    font-family:'Cormorant Garamond',serif;font-style:italic;font-size:1.15rem;
    color:var(--ink);background:rgba(255,255,255,.86);
    box-shadow:inset 0 0 0 1px var(--ink);
    opacity:0;transition:opacity .18s ease;
}
.map-wrap.is-over .map-drop{opacity:1}
.map-wrap.is-over{border-color:var(--accent)}

/* ─────────────────── approximate-geocode warning ─────────────────── */
.site-warn{
    border:1px solid var(--warn);margin-top:14px;padding:.85rem 1.1rem;
    color:var(--dim);font-size:.84rem;line-height:1.55;
}
.site-warn b{color:var(--warn);font-weight:400;display:block;margin-bottom:.28rem}
/* Two warnings can be true at once -- an imprecise pin AND a site the size of a
   district, which is exactly how one gets the other. A rule between them keeps
   them from reading as one run-on paragraph. */
.warn-sep{display:block;height:1px;background:var(--line);margin:.8rem 0}

/* ── project name, in the order rail ─────────────────────────────────────── */
.rail-name{padding:1.2rem 0;border-bottom:1px solid var(--line)}
.rn-k{display:block;font-size:.54rem;letter-spacing:.34em;text-indent:.34em;
    text-transform:uppercase;color:var(--faint);margin-bottom:9px}
/* "optional" is set apart rather than shouted: it is a qualifier on the label,
   not another label. */
.rn-k i{font-family:'Cormorant Garamond',Georgia,serif;font-style:italic;
    font-size:.8rem;letter-spacing:0;text-indent:0;text-transform:none;
    color:var(--faint);margin-left:.35rem}
.rail-name input{width:100%;background:transparent;color:var(--ink);
    border:0;border-bottom:1px solid var(--line);font-family:inherit;
    font-size:.86rem;padding:6px 2px;transition:border-color .2s ease}
.rail-name input:focus{outline:none;border-bottom-color:var(--accent)}
.rail-name input::placeholder{color:var(--faint)}
/* A taken name is a warning, not an error: the order still goes through, and
   the message says what will happen rather than refusing. */
.rn-msg{min-height:1rem;margin-top:7px;font-size:.66rem;color:var(--faint);line-height:1.4}
.rn-msg.warn{color:var(--warn)}

/* ── Google Places suggestions ─────────────────────────────────────────────
   The dropdown is appended to <body> by the Maps script, outside this page's
   stacking contexts, so it has to be styled and raised here rather than beside
   the field. 900 clears the map overlay at 700; Leaflet's own panes sit far
   below that. Google's default card has a blue highlight and a heavy shadow;
   here it is a hairline plate like every other on the page. */
.pac-container{
    z-index:900;
    background:var(--bg);
    border:1px solid var(--line);
    border-top:none;
    border-radius:0;
    box-shadow:0 10px 28px rgba(0,0,0,.08);
    font-family:inherit;
}
.pac-item{
    padding:7px 11px;
    border-top:1px solid var(--line);
    font-size:.72rem;
    color:var(--faint);
    cursor:pointer;
}
.pac-item:first-child{border-top:none}
.pac-item:hover,.pac-item-selected,.pac-item-selected:hover{
    background:rgba(0,0,0,.04);
    color:var(--ink);
}
.pac-item-query{font-size:.74rem;color:var(--ink)}
.pac-matched{font-weight:600}
/* The "powered by Google" strip is required by the Places terms; kept, in grey. */
.pac-logo:after{
    background-color:transparent;
    filter:grayscale(1) opacity(.7);
}

/* ── My projects ────────────────────────────────────────────────────────────
   One list, one instrument: hairline rows sharing their rules, the ground each
   site covers at the left, and the controls above answering at once. */
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}
.projects{max-width:1120px;margin:0 auto;padding:calc(var(--bar-h) + 46px) 40px 110px}
.pj-head{display:flex;align-items:baseline;justify-content:space-between;gap:20px;margin-bottom:26px}
.pj-head h2{font-family:'Cormorant Garamond',serif;font-weight:300;font-size:2.4rem;line-height:1.05}
.pj-count{font-size:.62rem;letter-spacing:.2em;text-transform:uppercase;color:var(--faint);white-space:nowrap}
.pj-tools{display:flex;align-items:center;justify-content:space-between;gap:18px 28px;flex-wrap:wrap;
    padding:10px 0;border-top:1px solid var(--line)}
.pj-filters{justify-content:flex-start;border-top:0;padding-top:0;padding-bottom:16px}
.pj-search{flex:1 1 280px;max-width:460px}
.pj-search input{width:100%;background:transparent;border:0;border-bottom:1px solid var(--line-strong);
    color:var(--ink);font-family:'Cormorant Garamond',serif;font-style:italic;font-size:1.22rem;
    padding:.3rem .1rem;outline:none;border-radius:0;-webkit-appearance:none;transition:border-color .3s ease}
.pj-search input:focus{border-bottom-color:var(--ink)}
.pj-search input::placeholder{color:var(--faint)}
.pj-seg{display:flex;align-items:center;gap:2px;flex-wrap:wrap}
.pj-k{font-size:.52rem;letter-spacing:.28em;text-transform:uppercase;color:var(--faint);margin-right:8px}
.pj-seg button{background:none;border:0;cursor:pointer;font-family:'Oxygen',sans-serif;font-size:.6rem;
    letter-spacing:.16em;text-transform:uppercase;color:var(--faint);padding:6px 8px;transition:color .2s ease}
.pj-seg button:hover{color:var(--ink)}
.pj-seg button.on{color:var(--ink);text-decoration:underline;text-underline-offset:5px;text-decoration-thickness:1px}
.pj-list{border-top:1px solid var(--line)}
.pj-row{position:relative;border-bottom:1px solid var(--line);transition:opacity .28s ease,transform .28s ease}
.pj-row.leaving{opacity:0;transform:translateX(-10px)}
.pj-open{display:grid;grid-template-columns:76px minmax(0,2.2fr) minmax(0,.9fr) minmax(0,1.5fr) 96px;
    align-items:center;gap:22px;padding:12px 110px 12px 0;text-decoration:none;color:inherit}
.pj-open:hover .pj-name{text-decoration:underline;text-underline-offset:4px;text-decoration-thickness:1px}
.pj-open:hover{background:var(--sel)}
.pj-thumb{width:76px;height:76px;background-color:#eee;display:block}
.pj-main{min-width:0;display:flex;flex-direction:column;gap:3px}
.pj-name{font-family:'Cormorant Garamond',Georgia,serif;font-size:1.3rem;line-height:1.2;color:var(--ink);
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pj-label{font-size:.7rem;color:var(--faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pj-owner{font-size:.54rem;letter-spacing:.16em;text-transform:uppercase;border:1px solid var(--line-strong);
    padding:1px 5px;margin-right:7px;color:var(--dim)}
.pj-spec{display:flex;flex-direction:column;gap:2px;font-size:.7rem;color:var(--dim);font-variant-numeric:tabular-nums}
.pj-spec span:first-child{font-size:.56rem;letter-spacing:.2em;text-transform:uppercase;color:var(--ink)}
.pj-state{display:flex;flex-direction:column;gap:6px;min-width:0}
.pj-bar{height:1px;background:var(--line);overflow:hidden;display:block}
.pj-bar i{display:block;height:100%;background:var(--ink);transform-origin:left}
.pj-now{font-size:.68rem;color:var(--dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pj-row.ready .pj-now{color:var(--ink)}
.pj-row.held .pj-now{color:var(--warn)}
.pj-date{font-size:.66rem;color:var(--faint);text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
/* Delete is a word at the row's end, quiet until the row is under the pointer. It
   asks once, inline, and says what deleting means. */
.pj-act{position:absolute;right:0;top:50%;transform:translateY(-50%);display:flex;align-items:center}
.pj-act button{background:none;border:0;cursor:pointer;font-family:'Oxygen',sans-serif;font-size:.56rem;
    letter-spacing:.18em;text-transform:uppercase;color:var(--faint);padding:6px 4px}
.pj-del{opacity:0;transition:opacity .2s ease,color .2s ease}
.pj-row:hover .pj-del,.pj-del:focus{opacity:1}
.pj-del:hover{color:var(--warn)}
.pj-confirm{display:none;align-items:center;gap:6px;background:#fff;padding:6px 0 6px 14px;
    font-family:'Cormorant Garamond',serif;font-style:italic;font-size:.98rem;color:var(--dim);white-space:nowrap}
.pj-row.confirming .pj-del,.pj-row.confirming .pj-ren,.pj-row.confirming .pj-share{display:none}
/* Share (sharing.py) sits beside Rename, as quiet; it opens the project's share page. */
.pj-share{opacity:0;transition:opacity .2s ease;font-family:'Oxygen',sans-serif;font-size:.56rem;letter-spacing:.18em;
    text-transform:uppercase;color:var(--faint);text-decoration:none;padding:6px 4px}
.pj-row:hover .pj-share,.pj-share:focus{opacity:1}
.pj-share:hover{color:var(--ink)}
/* Rename sits beside Delete, as quiet; it opens a field in place. */
.pj-ren{opacity:0;transition:opacity .2s ease}
.pj-row:hover .pj-ren,.pj-ren:focus{opacity:1}
.pj-rename{display:none;align-items:center;gap:6px;background:#fff;padding:6px 0 6px 14px}
.pj-rename .pj-new{font-family:'Cormorant Garamond',Georgia,serif;font-size:1.05rem;color:var(--ink);border:0;
    border-bottom:1px solid var(--line, #ccc);padding:2px 0;width:16rem;background:transparent;outline:none}
.pj-rename .pj-save{color:var(--ink)}
.pj-row.renaming .pj-rename{display:flex}
.pj-row.renaming .pj-ren,.pj-row.renaming .pj-del,.pj-row.renaming .pj-share{display:none}
.pj-row.confirming .pj-confirm{display:flex}
.pj-confirm .pj-yes{color:var(--warn)}
.pj-confirm .pj-no{color:var(--ink)}
.pj-row.deleted .pj-name,.pj-row.deleted .pj-now{color:var(--faint)}
span.pj-open:hover{background:none}
span.pj-open:hover .pj-name{text-decoration:none}
.pj-act .pj-restore{color:var(--ink)}
.pj-empty{font-family:'Cormorant Garamond',Georgia,serif;font-style:italic;font-size:1.15rem;color:var(--dim);padding:26px 2px}
.pj-empty a{color:var(--ink)}
/* Why a row came back after its delete or restore failed. Sits inside the row,
   not over the page: it is about that one project. */
.pj-msg{grid-column:1/-1;font-size:.78rem;color:var(--warn);padding:.3rem 2px 0}
@media(hover:none){.pj-del,.pj-ren,.pj-share{opacity:1}}
@media(max-width:900px){
    .projects{padding:calc(var(--bar-h) + 28px) 16px 80px}
    .pj-head h2{font-size:1.9rem}
    .pj-open{grid-template-columns:64px minmax(0,1fr) auto;grid-template-areas:'thumb main date' 'thumb state state';
        gap:6px 14px;padding:12px 0}
    .pj-thumb{grid-area:thumb;width:64px;height:64px}
    .pj-main{grid-area:main}
    .pj-spec{display:none}
    .pj-state{grid-area:state}
    .pj-date{grid-area:date}
    .pj-act{position:static;transform:none;justify-content:flex-end;padding:0 0 8px}
    .pj-confirm{white-space:normal}
}

/* ───────────────── the subscription, in the order rail and the manifest ─────────────────
   A manifest row that needs a subscription is real but not yet available, which is the
   three-state grammar's middle state: opacity plus saturate(0), because nothing
   interactive on this platform is ever grey. It is never removed from the manifest, since
   a reader has to see what they would get. */
.inc-locked{opacity:.62;filter:saturate(0)}
.inc-locked .inc-name{text-decoration:none}

/* The tickbox itself. The only genuine choice in the rail, so the only control in it. */
.rail-sub{display:flex;gap:9px;align-items:flex-start;margin-top:14px;padding:12px 13px;
    border:1px solid var(--line);cursor:pointer;transition:border-color .2s ease}
.rail-sub:hover{border-color:var(--line-strong)}
.rail-sub:focus-within{border-color:var(--ink)}
.rail-sub input{appearance:none;-webkit-appearance:none;width:13px;height:13px;flex:0 0 auto;
    margin-top:3px;border:1px solid var(--line-strong);background:var(--bg);cursor:pointer}
/* Selected is drawn by weight, never by hue, like every other control here. */
.rail-sub input:checked{background:var(--ink);border-color:var(--ink)}
.rail-sub input:focus-visible{outline:1px solid var(--ink);outline-offset:2px}
.rail-sub-body{display:flex;flex-direction:column;gap:3px}
.rail-sub-body b{font-weight:400;font-size:.8rem;color:var(--ink)}
.rail-sub-note{font-size:.68rem;line-height:1.5;color:var(--faint)}
.rail-sub input:checked ~ .rail-sub-body .rail-sub-note{color:var(--dim)}
