/* portrait.css — the three portrait looks.
 *
 * Loads after fonts.css, kit.css and ground.css. Everything inside .stage is
 * authored in CANVAS px on a 1080 x --ch stage: the kiosk runs 2160 x 3840, so
 * one canvas px is two device px and the whole composition scales by a single
 * transform on the stage.
 *
 * The wall's rules are the rules here. The photograph is the card, not a
 * passenger in a field of colour. The accent survives as a 14 px edge and a
 * kind chip, under 2% of a card's area. White type at full strength over a
 * Blue 100 scrim on the deck's own mountain panorama. Type never shrinks to
 * fit — copy is dropped whole. Only the ground parallaxes; everything that
 * represents time runs at exactly card speed.
 */

/* ══ page chrome ═══════════════════════════════════════════════════════════ */

.looks{display:flex;flex-wrap:wrap;gap:10px;margin:0 0 16px}
.looks button{display:flex;flex-direction:column;align-items:flex-start;gap:3px;
  flex:1 1 220px;padding:13px 17px;text-align:left;border:1px solid var(--line);
  border-radius:12px;background:var(--surface);color:var(--fg);cursor:pointer;
  box-shadow:var(--shadow);transition:background .15s,border-color .15s}
.looks button:hover{border-color:var(--accent)}
/* Display 1 / Display 2 — the stable, factual heading. Matches the .rcard
   eyebrow (.k) elsewhere on this page: small, uppercase, dim. The friendly
   name lives one level down in .dname, because it is data (from data-name,
   later overwritten from the database) and not the thing the button is. */
.looks button b{font-size:11px;letter-spacing:.13em;text-transform:uppercase;
  color:var(--fg-dim);font-weight:700}
/* .dname — the friendly name. Takes the prominent slot the ordinal-prefixed
   <b> used to hold. */
.looks button .dname{font-family:var(--display);font-weight:400;font-size:21px;
  letter-spacing:-.01em;margin-top:2px}
/* .dnote — short and factual, never marketing copy. */
.looks button .dnote{font-size:12.5px;color:var(--fg-dim);line-height:1.38}
.looks button[aria-pressed="true"]{background:var(--accent);border-color:var(--accent)}
.looks button[aria-pressed="true"] b,
.looks button[aria-pressed="true"] .dname,
.looks button[aria-pressed="true"] .dnote{color:#fff}

/* ── rename the selected display — issue remove-ai-text-fluff ────────────
   One row, same widths and gaps as .looks/.opens so the block reads as part
   of the same column rather than a new idea. --accent is NOT used for text
   or the input border here: kit.css:30 leaves it undefined in the default
   (non-data-theme, light-preference) :root, so anything load-bearing for
   contrast is built from --fg/--surface/--line instead, which are always
   defined in every theme branch. The status colours below borrow the same
   --vw-* brand tokens .ed-msg already uses (editor/panel.css) — those are
   set once in kit.css's un-overridden :root and hold their value in both
   themes, unlike --accent. */
.rename{display:flex;flex-wrap:wrap;align-items:center;gap:8px 12px;
  margin:0 0 20px;padding:12px 15px;border:1px solid var(--line);
  border-radius:12px;background:var(--surface);box-shadow:var(--shadow)}
.rename label{flex:0 0 auto;font-size:11px;letter-spacing:.13em;
  text-transform:uppercase;color:var(--fg-dim);font-weight:700}
.rename input{flex:1 1 200px;min-width:140px;height:32px;padding:0 11px;
  font:inherit;font-size:13.5px;color:var(--fg);background:var(--bg);
  border:1px solid var(--line);border-radius:8px}
.rename input:disabled{color:var(--fg-dim);cursor:not-allowed}
.rename input:focus-visible{outline:2px solid var(--fg);outline-offset:1px}
.rename-msg{flex:1 1 100%;margin:0;font-size:12px;color:var(--fg-dim)}
.rename-msg.ok{color:var(--vw-green)}
.rename-msg.warn{color:var(--vw-orange)}
.rename-msg.err{color:#E5484D}

/* ── Open display — issue #93 ─────────────────────────────────────────────
   A quieter row under the picker, deliberately: the picker chooses which
   screen you are LOOKING at and is the loud control; this opens that screen
   as its own window on a second monitor, which is a thing you do once a
   session. Same widths and gaps so the two rows line up as one block.
   Disabled is a real state here — the offline builder has no player URL to
   open — so it has to read as unavailable rather than broken.             */
.opens{display:flex;flex-wrap:wrap;gap:10px;margin:0 0 20px}
.opens .openbtn{display:inline-flex;align-items:center;gap:8px;
  flex:1 1 220px;padding:9px 15px;font:inherit;font-size:13.5px;
  text-align:left;text-decoration:none;border:1px dashed var(--line);
  border-radius:12px;background:transparent;color:var(--fg-dim);
  cursor:pointer;transition:border-color .15s,color .15s}
.opens .openbtn:hover{border-color:var(--accent);color:var(--fg)}
.opens .openbtn:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.opens .openbtn[disabled]{cursor:not-allowed;opacity:.5}
.opens .openbtn[disabled]:hover{border-color:var(--line);color:var(--fg-dim)}
.opens .openb-i{font-size:15px;line-height:1}

/* ── the client review row ────────────────────────────────────────────────
   The same affordance one row down, so the pair reads as "open this display"
   / "send this display to the client". It borrows every rule above by sharing
   .opens and .openb-i; the only difference is the accent, because one of these
   opens a window on the desk and the other hands the unreleased timeline to
   somebody outside the building. .opens on its own keeps the 20px gap, so the
   two rows sit as two rows rather than one crowded eight-button block. */
.opens.reviews{margin-top:-8px}
.opens .reviewbtn{display:inline-flex;align-items:center;gap:8px;
  flex:1 1 220px;padding:9px 15px;font:inherit;font-size:13.5px;
  text-align:left;text-decoration:none;border:1px dashed var(--line);
  border-radius:12px;background:transparent;color:var(--fg-dim);
  cursor:pointer;transition:border-color .15s,color .15s}
.opens .reviewbtn:hover{border-color:var(--vw-blue-30);color:var(--fg)}
.opens .reviewbtn:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.opens .reviewbtn[disabled]{cursor:not-allowed;opacity:.5}
.opens .reviewbtn[disabled]:hover{border-color:var(--line);color:var(--fg-dim)}

/* minmax(0,1fr), never a bare 1fr: the panel column holds the entries strip,
   which is nineteen 172 px chips — 3.6 kpx of content that scrolls sideways
   inside its own box. A 1fr track takes its automatic minimum from that
   content instead of from the page, so the bare version widened the column to
   3.6 kpx and took the whole document with it. Same reason min-width:0 on the
   items: a grid item's automatic minimum is content too. */
.split{display:grid;gap:26px;grid-template-columns:minmax(300px,440px) minmax(0,1fr);
  align-items:start}
.split>*{min-width:0}
/* The display follows you down the panel. A 9:16 canvas capped at 560 px is
   ~782 px tall, which fits under a laptop's chrome with room to spare, so it
   can stay put while several thousand px of editor scroll past beside it.
   This only travels because align-items:start above leaves the preview
   start-aligned in a grid area as tall as the panel column — a sticky grid
   item stretched to its own row height has nowhere to go. */
.previewcol{position:sticky;top:16px}

/* ── the pinned rail's instruments ────────────────────────────────────────
   The transport, the canvas toggle and the five readout cards live under the
   preview now (Cam, 2026-08-10) so the display and everything that measures
   it travel together. In a 440 px column at their page sizes they run to
   ~500 px, which would leave a laptop's screen almost nothing for the canvas
   — so in here they are set tight, and fit() scales the canvas to whatever
   they leave. On a desktop monitor nothing is given up: the canvas is capped
   at 560 px wide anyway and the height never binds. */
/* The four control rows read as ONE card here rather than four: four separate
   cards spend ~100 px of the column on gaps and doubled padding, and that is
   height taken straight off the canvas. */
.previewcol .ctl{margin-top:0;padding:7px 12px;gap:7px 12px;border-radius:0;
  border-top:none;box-shadow:none}
.previewcol .frame+.ctl{margin-top:12px;border-top:1px solid var(--line);
  border-radius:12px 12px 0 0}
.previewcol .ctl:has(+.readout){border-radius:0 0 12px 12px}
.previewcol .ctl input[type=range]{flex:1 1 80px;width:auto;min-width:80px}
.previewcol .ctl button{padding:5px 10px}
.previewcol .ctl select{max-width:150px}
.previewcol .ctl:last-child{border-radius:0 0 12px 12px}
/* The readout is NOT in here — see the note in page.html. Five cards two
   across cost 283 px of the rail, and the rail's height is the canvas's.
   A flex column so the readout can be ordered: beside the preview when the
   columns are side by side, and out of the way when they stack. */
.panelcol{display:flex;flex-direction:column}
.panelcol>*{min-width:0}
.panelcol .readout{order:-1;margin-top:0;margin-bottom:16px}

/* ── the editor's own running order ───────────────────────────────────────
   The panel ships with 405 px of export, message holders and the background
   picker ahead of the Entries strip, so on the projection — where the columns
   stack — the thing you came to edit began 1450 px down the page. Cam,
   2026-08-10: the entries come directly under the preview.
     Ordered rather than re-authored: every one of these blocks is addressed
   by id from editor.js and the smoke tests, and panel.js's markup is shared
   with the two wall pages, which do not load this sheet. Ordering here moves
   nothing and breaks nothing.
     The `*` rule is the load-bearing one: a flex child with no `order` sits
   at 0, so anything unlisted — including whatever the panel grows next —
   would jump ABOVE the strip. At 5 it falls in with the rest and keeps its
   authored position within it.
     NOTHING is above the strip. The pane header and the sign-in block were,
   until 2026-08-11 — the header because it names the pane, sign-in because it
   is how the client reaches the shared timeline and a first visit that cannot
   see it concludes there is nothing to sign in to. Cam's call, twice asked
   for: the entries go directly under the preview and everything else is
   below them. Sign-in is now the last section on the page, under `Show
   content`, and a signed-out visitor scrolls to find it. */
#pane-edit{display:flex;flex-direction:column}
#pane-edit>*{order:5}
#pane-edit>.ed-carousel{order:0}
#pane-edit>.ed-detail{order:1}
/* No sticky once the columns stack: the preview would then be a wall of
   screen between you and the field you are typing in. */
@media (max-width:880px){
  .split{grid-template-columns:minmax(0,1fr)}
  .previewcol{position:static}
}
/* the projection is 4.16:1: beside a column of controls it is 134 px tall
   and worth nothing to look at, so it takes the page and the controls drop
   beneath it. Stacked, there is no second column to scroll past it, so
   sticky comes off here too. */
.split.wide{grid-template-columns:minmax(0,1fr)}
.split.wide .previewcol{position:static}
/* Stacked, there is no second column, so everything between the wall and the
   Entries strip is height you scroll through before you can edit anything.
   Cam, 2026-08-10: the editor comes DIRECTLY under the projection preview.
     So the readout goes below the editor here (it reports, it does not
   drive), and the control rows stay under the preview but keep the rail's
   tight setting rather than reverting to page sizes — at full width each row
   fits on one line, so the whole block is ~150 px and the strip clears a
   laptop's fold. Only the range goes back to its natural size: stretching a
   slider across 1400 px makes a 1 % nudge a 14 px mouse move. */
.split.wide .previewcol .ctl input[type=range]{flex:0 0 auto;width:132px}
.split.wide .previewcol .ctl select{max-width:none}
.split.wide .panelcol .readout{order:1;margin-top:16px;margin-bottom:0}

.frame{position:relative;background:var(--vw-navy);border-radius:14px;
  overflow:hidden;box-shadow:var(--shadow)}
.stage{position:absolute;left:0;top:0;width:1080px;height:1920px;--ch:1920px;
  transform-origin:0 0;overflow:hidden;background:var(--vw-blue-100);
  font-family:var(--body);
  /* ── the ground, as properties ────────────────────────────────────────
     The background behind the show is AUTHORABLE per display (issue #128):
     engine.js writes these four onto the stage at mount from the pack's own
     `backgrounds`, and the values below are what the shipped show has always
     drawn — the deck's mountain panorama under a Blue 100 wash. A page with
     no authored ground sets none of them and gets exactly this.

     --gnd-size is the whole reason the portrait finally fills: the panorama
     is scaled to the GROUND'S OWN HEIGHT and tiled sideways, so it covers
     whatever canvas it is given instead of being drawn at a fixed 1200 px
     and centred. On the 1200 px wall `auto 1200px` is the panorama at its
     native size, which is what the wall drew before and still draws. */
  --gnd-color:#0033B5;
  --gnd-img:var(--waveDeep);
  --gnd-size:auto var(--ch);
  --gnd-wash:var(--vw-blue-100);
  --gnd-wash-o:.56}
.stage.paused *{animation-play-state:paused}

/* Grab-to-scrub. The class is written by engine.js only on a shell whose
   transport can start the clock again, so the cursor never promises a gesture
   the page has not wired. It rides on .frame rather than .stage because
   mount() rewrites the stage's whole className on every display switch.
     `touch-action:none` is the load-bearing half on a touchscreen: without it
   a drag across the canvas scrolls the review page and the timeline never
   moves. It is set on the ancestor deliberately — a touch is governed by the
   values from the hit element up, so one declaration covers every card,
   photograph and year on the canvas. `cursor` is inherited and reaches them
   the same way. */
.frame.scrubbable{cursor:grab;touch-action:none}
.frame.scrubbable.grabbing{cursor:grabbing}

/* ══ shared canvas layers ══════════════════════════════════════════════════ */

/* ── ground ──────────────────────────────────────────────────────────────
   FULL BLEED. Until 2026-08-10 this was a horizon: an 820 px band on the foot
   of the picture, drawing the panorama at a fixed 1936x1200 with an opaque
   sky gradient filling the 1100 px above it. On the 4.16:1 wall that reads as
   the range it is; stood upright on a 1080x1920 kiosk it read as a strip of
   mountains across the middle of an otherwise empty blue screen, which is
   what Cam called out. The ground now covers the canvas — scaled to the
   canvas's own height by --gnd-size and tiled sideways — and the sky is a
   TINT over it rather than a separate band beside it.

   Whether it MOVES is the look's call, not this rule's: the wall and The
   Window pan it at 0.14x, Ascent holds it still (Cam, 2026-08-10 — the drift
   is a sideways mechanic and Ascent's cards travel down). Where it does pan,
   the tile repeats on X only — tiling it vertically would stack mountains on
   sky — and looks.js reads the scaled tile width off P.gndTile for its wrap
   modulus. Hard-coding 1936 here again would make any ground that is not the
   shipped panorama jump.                                                    */
.g-ground{position:absolute;inset:0;z-index:1;
  background:var(--gnd-color) var(--gnd-img) repeat-x center/var(--gnd-size)}
.g-ground::after{content:"";position:absolute;inset:0;
  background:var(--gnd-wash);opacity:var(--gnd-wash-o)}
/* the sky, now a TINT rather than a mask. It used to be opaque, and it could
   afford to be: it filled the 1100 px above an 820 px ground band, where
   there was no picture to hide. Over a full-bleed ground the same gradient
   would hide the top two-thirds of it, which is the thing #128 was asked to
   fix. What carries white type over a photograph is the ground's own 56%
   Blue 100 wash — that is the wall's arrangement, over the same panorama,
   and the wall is signed off. This is depth on top of it, nothing more. */
.g-sky{position:absolute;inset:0;z-index:2;pointer-events:none;
  background:linear-gradient(180deg,rgba(0,56,190,.42) 0,
  rgba(0,77,255,.20) 38%,rgba(0,77,255,.06) 66%,rgba(0,77,255,0) 88%)}
.g-atmos{position:absolute;left:0;right:0;bottom:0;height:262px;z-index:26;
  pointer-events:none;background:linear-gradient(180deg,rgba(0,24,104,0),
  rgba(0,20,92,.60) 46%,rgba(0,11,64,.96))}
/* The deck's top-left wordmark came off the wall on 2026-07-31 at Cam's
   request; it is off here too. --markWhite stays available in kit.css. */

/* ══ card atoms ════════════════════════════════════════════════════════════
   Wall -> portrait: heading 98 -> 76, body 52 -> 34, chip 42 -> 26,
   date 48 -> 32. The ratios between them are the deck's and are unchanged;
   only the absolutes come down, because the measure came down from a 1040 px
   column on a 25 ft wall to a ~900 px one on a screen a guest walks up to.
   The travelling year is set per look — it has no 25 ft rail to sit on any
   more — at 76 in Ascent's column and 190 across the foot in The March. The
   Window keeps the wall's own 232, because it IS the wall.                  */
.c-h{margin:0;font-family:var(--display);font-weight:400;font-size:76px;
  line-height:1.07;letter-spacing:0;color:#fff;text-wrap:balance}
.c-h em{font-style:italic}                      /* the deck's signature move */
/* the statement step-down — engine.js STATEMENT. A heading long enough to be
   a sentence rather than a name is set smaller so it fits the column whole,
   and the line beneath it drops with it so an attribution reads as an
   attribution rather than as a second headline. */
.c-h.xl{font-size:47px;line-height:1.14}
.c-h.xl+.c-b{font-size:26px;margin-top:26px}
/* No left indent: round-1 feedback asked for the title line and the subline
   to sit on the same edge as the tagline chip ("Founding", "Innovation").
   The body used to start 24px in on Ascent and 30px in on the wall, so the
   card had three left edges where it should have had one. */
.c-b{margin:22px 0 0 0;font-family:var(--body);font-size:34px;line-height:1.34;
  color:#fff}
.c-foot{display:flex;align-items:center;flex-wrap:wrap;gap:18px;margin-top:auto;
  padding-top:26px}
.c-kind{flex:0 0 auto;background:var(--acc);color:var(--vw-navy);padding:8px 17px;
  font:700 26px/1.16 var(--body);letter-spacing:.02em;text-transform:uppercase}
.c-date{font-family:var(--display);font-size:32px;line-height:1.1;color:#fff}
.c-date.prov{border-bottom:3px dashed currentColor;padding-bottom:5px;font-style:italic}
.c-note{width:100%;margin:2px 0 0;font-family:var(--body);font-style:italic;
  font-size:26px;line-height:1.2;color:rgba(255,255,255,.86)}

/* ── the photograph ─────────────────────────────────────────────────────── */
.c-shot{flex:0 0 auto}
.c-plate{display:flex;align-items:stretch}
.c-edge{flex:0 0 14px;background:var(--acc);border-radius:7px;margin-right:20px}
.c-frames{display:flex;align-items:center;min-width:0}
.c-frames.col{flex-direction:column;align-items:flex-start}
.c-photo{position:relative;flex:0 0 auto;overflow:hidden;background:rgba(7,10,18,.30)}
.c-photo img{position:absolute;inset:0;width:100%;height:100%;display:block;
  object-fit:cover}
.c-photo.empty{background:repeating-linear-gradient(135deg,rgba(255,255,255,.20) 0 24px,
  rgba(255,255,255,.04) 24px 48px),rgba(255,255,255,.46)}
.c-photo.empty i{position:absolute;inset:10px;border:3px dashed rgba(7,10,18,.28);
  border-radius:10px}

/* ── rail furniture, shared ─────────────────────────────────────────────── */
.r-line{position:absolute;background:rgba(255,255,255,.26);border-radius:2px}
.r-tick{position:absolute;left:0;top:0}
.r-tick i{position:absolute;background:rgba(255,255,255,.42)}
.r-yr{position:absolute;left:0;top:0;color:#fff}
.r-span{position:absolute;background:currentColor;opacity:.34;border-radius:6px}
.r-hold{position:absolute;left:0;top:0}
.r-dot{position:absolute;border-radius:50%;background:currentColor;
  box-shadow:0 0 0 8px rgba(255,255,255,.14),0 0 34px rgba(255,255,255,.5)}
.r-num{font-family:var(--display);font-weight:400;line-height:1;color:#fff;
  text-shadow:0 6px 44px rgba(0,20,80,.5)}
.r-tbc{display:none;font:700 26px/1.14 var(--body);letter-spacing:.09em;
  text-transform:uppercase;color:#fff;opacity:.82}
.r-yr.tbc .r-num{font-style:italic;border-bottom:5px dashed;padding-bottom:5px}
.r-yr.tbc .r-tbc{display:block}

/* ══ LOOK 1 · ASCENT ═══════════════════════════════════════════════════════
   Drift turned ninety degrees. The decade climbs: 2016 leaves at the head of
   the spine and 2026 arrives from underneath, so reading down the spine still
   runs forward in time while the cards themselves rise. The spine is the
   rail, and it carries everything the wall's rail carried — ticks, the year's
   own span of it, and the travelling numeral that parks for as long as its
   year has cards to show.

   The wall reserves a 276 px band between its cards and its rail and lets
   nothing else into it, so the travelling numeral always has clear air to
   park in. Turned upright, that band becomes a COLUMN: spine at 60, years
   from 104, cards from --a-gut. It buys a year that is never reading through
   a line of body copy, and it costs only as much width as a four-digit year
   in 76 px serif actually takes — 288 px, tightened from 348 on 2026-08-10
   at Cam's mark-up, which pulled 60 px back into the cards.

   --a-gut is set by looks.js, not here: the gutter is one number and the
   card measure is derived from it (left:var(--a-gut) + right:48px), because
   it used to be four hand-copied literals and they had already drifted
   apart from the prose describing them.                                    */
.L1{--a-gut:288px}
.L1 .a-cards{position:absolute;inset:0;z-index:5}
.L1 .a-card{position:absolute;left:var(--a-gut);right:48px;top:0;display:flex;
  flex-direction:column}
.L1 .a-card .c-shot{margin-bottom:30px}
.L1 .a-txt{display:flex;flex-direction:column}
.L1 .a-spine{position:absolute;left:0;top:0;bottom:0;width:var(--a-gut);z-index:41;
  pointer-events:none}
.L1 .a-spine>.r-line{left:60px;top:110px;bottom:110px;width:4px}
.L1 .a-glow{position:absolute;left:60px;width:4px;top:0;height:780px;border-radius:2px;
  background:linear-gradient(180deg,rgba(255,255,255,0),rgba(255,255,255,.95) 39%,
  rgba(255,255,255,0))}
.L1 .a-win{position:absolute;left:0;top:0;width:var(--a-gut);height:var(--ch);overflow:hidden;
  -webkit-mask-image:linear-gradient(180deg,transparent 0,#000 120px,
    #000 calc(100% - 120px),transparent 100%);
  mask-image:linear-gradient(180deg,transparent 0,#000 120px,
    #000 calc(100% - 120px),transparent 100%)}
.L1 .a-ticks{position:absolute;inset:0}
.L1 .r-tick i{left:42px;top:-1px;width:38px;height:3px}
.L1 .r-tick.tbd i{background:none;height:0;border-top:3px dashed rgba(255,255,255,.85)}
.L1 .r-span{left:56px;width:12px;top:0}
.L1 .r-dot{left:45px;top:-17px;width:34px;height:34px}
/* the year column. Same masked window as the spine, so a year fades in and
   out at the ends of the rail rather than clipping against them. */
.L1 .a-yrs{position:absolute;left:0;top:0;width:var(--a-gut);height:var(--ch);z-index:40;
  overflow:hidden;
  -webkit-mask-image:linear-gradient(180deg,transparent 0,#000 120px,
    #000 calc(100% - 120px),transparent 100%);
  mask-image:linear-gradient(180deg,transparent 0,#000 120px,
    #000 calc(100% - 120px),transparent 100%)}
.L1 .a-yrlab{position:absolute;left:104px;top:0;display:flex;flex-direction:column;
  align-items:flex-start;white-space:nowrap;transform:translateY(-50%)}
.L1 .a-yrlab .r-num{font-size:76px}
.L1 .a-yrlab .r-tbc{font-size:22px;margin-top:8px}

/* ══ THE PROJECTION ════════════════════════════════════════════════════════
   The 4992 x 1200 wall, in the wall's own canvas px. TWO displays draw it —
   Display 1 at its own size and The Window scaled into a 420 px strip — so
   everything INSIDE .w-wall is scoped to .w-wall alone and nothing in here
   names a look. Only where the wall SITS, and what surrounds it, belongs to
   a look. Written once, drawn twice; see buildWall() in looks.js.          */
/* The wall is 1200 px tall wherever it is drawn — at its own size on Display
   1, and inside a 0.35 strip in The Window — so its ground pins the draw
   height here rather than reading the stage's --ch, which is 1920 under The
   Window. The IMAGE still comes off the stage, so an authored ground reaches
   both displays; only the height it is scaled to is the wall's own. */
.w-wall{position:absolute;left:0;top:0;width:4992px;height:1200px;
  transform-origin:0 0;--gnd-size:auto 1200px}
.w-wall .w-ground{position:absolute;inset:0;z-index:1;
  background:var(--gnd-color) var(--gnd-img) repeat-x center/var(--gnd-size)}
.w-wall .w-ground::after{content:"";position:absolute;inset:0;
  background:var(--gnd-wash);opacity:var(--gnd-wash-o)}
.w-wall .w-cards{position:absolute;inset:0;z-index:5}
.w-wall .wc{position:absolute;left:0;top:68px;height:754px;display:flex}
.w-wall .wc-txt{width:1040px;flex:0 0 1040px;display:flex;flex-direction:column;
  margin-right:64px;overflow:hidden}
.w-wall .wc-shot{flex:0 0 auto}
/* wall type — the projection's own scale, not the portrait one */
.w-wall .c-h{font-size:98px}
.w-wall .c-h.xl{font-size:61px;line-height:1.14}
.w-wall .c-b{font-size:52px;line-height:1.3;margin:30px 0 0 0}
.w-wall .c-h.xl+.c-b{font-size:36px;margin-top:34px}
.w-wall .c-kind{font-size:42px;padding:11px 24px}
.w-wall .c-date{font-size:48px}
.w-wall .c-foot{gap:28px;padding-top:0}
.w-wall .c-edge{flex-basis:14px;margin-right:28px}
/* the deck's 1-plus-2 grid (issue #102) — the wall's own arrangement for a
   3-photo card, mirrored from concept-c.css's .d-frames.grid/.d-pair now
   that buildWall() can reach it through the shared geom(). */
.w-wall .c-frames.grid{flex-direction:column}
.w-wall .c-frames.gridrow{flex-direction:row}
.w-wall .c-pair{display:flex;align-items:center;gap:26px}
.w-wall .c-frames.gridrow .c-pair{flex-direction:column}
.w-wall .w-rail{position:absolute;inset:0;z-index:40}
.w-wall .w-rail>.r-line{left:0;right:0;top:1140px;height:4px}
.w-wall .w-ticks,.w-wall .w-years{position:absolute;left:0;top:0;
  width:4992px;height:1200px;overflow:hidden}
.w-wall .w-ticks .r-tick i{left:-2px;top:1131px;width:4px;height:22px}
.w-wall .w-ticks .r-tick.tbd i{width:0;background:none;
  border-left:4px dashed rgba(255,255,255,.85)}
.w-wall .w-years .r-span{left:0;top:1136px;height:12px}
.w-wall .w-years .r-dot{left:-19px;top:1121px;width:38px;height:38px}
.w-wall .w-yrlab{position:absolute;left:-14px;top:864px;display:flex;
  align-items:flex-end;gap:18px;white-space:nowrap}
.w-wall .w-yrlab .r-num{font-size:232px}
.w-wall .w-yrlab .r-tbc{font-size:44px;padding-bottom:28px}

/* ══ DISPLAY 1 · THE PROJECTION, AT ITS OWN SIZE ═══════════════════════════
   The stage IS the wall here — 4992 x 1200, no scale of its own beyond the
   one the page fits it into. No furniture at all: the focus-axis rule used
   to be drawn here, and it ran the full height of the picture up from the
   year rail's dot and across whatever photograph was passing. The axis is
   still where it was, it is just not marked.                               */
.L0{background:var(--vw-blue-100)}

/* ══ LOOK 2 · THE WINDOW ═══════════════════════════════════════════════════
   The projection itself, cut to a portrait screen. The strip across the top
   is the 4992 x 1200 wall running at its own scale, its own type sizes and
   its own speed, rail and all — the same show a guest saw on the terrace.
   Under it, whatever is parked on the focus axis is set out at reading size
   (the axis is not drawn — see DISPLAY 1).
   One clock drives both, so the card below always names what is in the strip
   above, and the sill between them carries that card's accent.             */
.L2 .w-strip{position:absolute;left:0;right:0;top:0;height:420px;z-index:5;
  overflow:hidden;background:var(--vw-blue-100)}
.L2 .w-wall{transform:scale(.35)}                         /* 420 / 1200 */
/* wide enough that a card entering the window arrives out of the blue rather
   than appearing with its heading already sliced in half */
.L2 .w-fade{position:absolute;top:0;height:420px;width:200px;z-index:20;
  pointer-events:none}
.L2 .w-fade.l{left:0;background:linear-gradient(90deg,rgba(0,77,255,.97),
  rgba(0,77,255,.72) 30%,rgba(0,77,255,0))}
.L2 .w-fade.r{right:0;background:linear-gradient(270deg,rgba(0,77,255,.97),
  rgba(0,77,255,.72) 30%,rgba(0,77,255,0))}
.L2 .w-sill{position:absolute;left:0;right:0;top:420px;height:6px;z-index:24;
  background:var(--acc)}
.L2 .w-read{position:absolute;left:84px;right:84px;top:486px;
  bottom:330px;z-index:8;display:flex;flex-direction:column;
  transition:opacity .4s ease}
.L2 .w-read.out{opacity:0}
.L2 .w-read .c-shot{margin-bottom:36px}
.L2 .w-txt{display:flex;flex-direction:column;flex:1 1 auto}
.L2 .w-foot{position:absolute;left:84px;right:84px;bottom:52px;z-index:40;
  display:flex;align-items:baseline;justify-content:space-between;gap:24px}
.L2 .w-count{font:700 24px/1.1 var(--body);letter-spacing:.18em;
  text-transform:uppercase;color:var(--vw-blue-30);font-variant-numeric:tabular-nums}

/* ══ LOOK 3 · THE MARCH ════════════════════════════════════════════════════
   The wall's mechanic, unchanged, with the card cut to portrait. Cards drift
   right to left past a focus axis near the left edge; each is nearly the
   width of the screen, so one card holds the frame while the next is already
   showing at the edge. The photograph owns the top of the card and the accent
   edge stands its full height, as it does on the wall. The 2016-2026 rail is
   where the wall keeps it: across the foot, at 95% of the canvas.

   One thing IS different, and it is the canvas that forces it. On the wall a
   card is a fifth of the picture and two or three are always whole; here a
   card is most of the screen, so under a constant drift it would spend nearly
   all its time half off the edge and never be readable. So the wall's own
   hold — the sticky clamp that parks a year numeral on the focus axis while
   its cards cross — is extended to the card itself: it glides in, parks on
   the axis long enough to be read, and glides out. Same axis, same easing,
   same 1:1 binding between the cards and the rail; only the pacing changes,
   and the two other looks keep the constant drift if that pacing is wanted. */
.L3 .m-cards{position:absolute;inset:0;z-index:5}
.L3 .m-card{position:absolute;left:0;display:flex;flex-direction:column}
.L3 .m-card .c-shot{margin-bottom:0}
.L3 .m-txt{flex:0 0 auto;display:flex;flex-direction:column;padding-top:36px}
.L3 .m-txt .c-foot{margin-top:0;padding-top:40px}
.L3 .m-fade{position:absolute;top:0;bottom:0;width:130px;z-index:20;pointer-events:none}
.L3 .m-fade.l{left:0;background:linear-gradient(90deg,rgba(0,42,140,.94),
  rgba(0,58,190,.58) 42%,rgba(0,77,255,0))}
.L3 .m-fade.r{right:0;background:linear-gradient(270deg,rgba(0,42,140,.94),
  rgba(0,58,190,.58) 42%,rgba(0,77,255,0))}
.L3 .m-rail{position:absolute;inset:0;z-index:40;pointer-events:none}
.L3 .m-rail>.r-line{left:0;right:0;top:calc(var(--ch) * .95);height:4px}
.L3 .m-glow{position:absolute;left:0;width:620px;top:calc(var(--ch) * .95);height:4px;
  border-radius:2px;background:linear-gradient(90deg,rgba(255,255,255,0),
  rgba(255,255,255,.95) 22%,rgba(255,255,255,0))}
.L3 .m-win{position:absolute;left:0;top:0;width:1080px;height:var(--ch);overflow:hidden;
  -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 100px,
    #000 calc(100% - 100px),transparent 100%);
  mask-image:linear-gradient(90deg,transparent 0,#000 100px,
    #000 calc(100% - 100px),transparent 100%)}
.L3 .m-ticks,.L3 .m-years{position:absolute;inset:0}
.L3 .m-ticks .r-tick i{left:-1px;top:calc(var(--ch) * .95 - 9px);width:3px;height:20px}
.L3 .m-ticks .r-tick.tbd i{width:0;background:none;
  border-left:3px dashed rgba(255,255,255,.85)}
.L3 .m-years .r-span{top:calc(var(--ch) * .95 - 4px);height:12px}
.L3 .m-years .r-dot{left:-17px;top:calc(var(--ch) * .95 - 15px);width:34px;height:34px}
.L3 .m-yrlab{position:absolute;left:-10px;top:calc(var(--ch) * .95 - 254px);
  display:flex;align-items:flex-end;gap:14px;white-space:nowrap}
.L3 .m-yrlab .r-num{font-size:190px}

/* ══ guides — design tools, never part of a look ═══════════════════════════ */
.guide{position:absolute;inset:0;z-index:60;pointer-events:none;display:none}
.stage.showguide .guide{display:block}
.guide .safe{position:absolute;left:54px;right:54px;top:54px;bottom:54px;
  border:2px dashed rgba(255,231,74,.72)}
.guide .lab{position:absolute;left:62px;bottom:22px;font:700 22px/1 var(--body);
  letter-spacing:.14em;text-transform:uppercase;color:rgba(255,231,74,.9)}
/* where a standing guest actually looks on a floor-standing portrait panel */
.guide .eye{position:absolute;left:0;right:0;top:calc(var(--ch) * .60);height:2px;
  background:rgba(1,207,118,.85)}
.guide .eyelab{position:absolute;right:62px;top:calc(var(--ch) * .60 - 34px);
  font:700 22px/1 var(--body);letter-spacing:.14em;text-transform:uppercase;
  color:rgba(1,207,118,.95)}
.stage.legcheck .too-small{outline:3px solid #E5484D;outline-offset:5px}

@media (prefers-reduced-motion:reduce){
  .L2 .w-read{transition:none}
}
