/* Project Office quality pass — v17 */
:root{--focus-ring:#edeef1}

/* Restore the designed ending and keep every interactive state visible. */
.final-room{display:grid!important}
:where(a,button):focus-visible{outline:2px solid var(--focus-ring);outline-offset:4px}
.room-door:focus-visible{outline-offset:2px}
[inert]{pointer-events:none!important}
.product-room[aria-hidden="true"],.website-room[aria-hidden="true"]{visibility:hidden!important}

/* Product suites use distinct room lighting while sharing one architectural shell. */
.finance-product{--suite-accent:114,224,196;--suite-secondary:107,151,255}
.habit-product{--suite-accent:133,226,171;--suite-secondary:205,236,144}
.fylunor-product{--suite-accent:183,166,255;--suite-secondary:128,116,214}
.medha-product{--suite-accent:244,183,118;--suite-secondary:232,151,196}
.website-room{--suite-accent:114,224,196;--suite-secondary:183,166,255}
.product-room .room-vignette.strong,.website-room .room-vignette.strong{
  background:
    radial-gradient(circle at 72% 27%,rgba(var(--suite-accent),.2),transparent 28%),
    radial-gradient(circle at 22% 78%,rgba(var(--suite-secondary),.13),transparent 33%),
    linear-gradient(90deg,rgba(10,11,13,.88),rgba(10,11,13,.56) 48%,rgba(10,11,13,.72));
}
.product-display::before{content:"";position:absolute;z-index:1;inset:0;pointer-events:none;background:linear-gradient(135deg,rgba(var(--suite-accent),.08),transparent 34%,rgba(var(--suite-secondary),.07));box-shadow:inset 0 1px rgba(255,255,255,.08)}
.product-status b{border-color:rgba(var(--suite-accent),.35);background:rgba(var(--suite-accent),.1);color:rgb(var(--suite-accent))}
.product-title img{box-shadow:0 18px 42px rgba(0,0,0,.36),0 0 0 1px rgba(var(--suite-accent),.24)}

/* Keep room content readable on short screens and long product names intact. */
.product-suite,.website-suite-room,.product-story,.product-display,.website-copy{min-width:0}
.product-story,.website-copy{overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.2) transparent}
.product-title>div{min-width:0}.product-title h2{overflow-wrap:anywhere}
/* overflow-wrap:anywhere above is the last-resort guard against a long name
   overflowing the story column - but it breaks mid-WORD, and it was firing:
   "HabitAroha" rendered as "HabitAroh / a" at 1100, 1280, 1440, 1600 and 1920.
   (That name is now "Habit Kramedha" and no longer appears in this tour at
   all; the envelope below is sized for "Finance Tracker", which at 15
   characters is the longest title any room here carries. Kept as written
   because it records the measurement that produced the clamp.)
   Only the two-column desktop layout is tight. Beside the 70px icon the title
   gets 187px at 1051 and 332px at 1600 (avail = 0.264vw - 90px), while the name
   costs ~4.91px of width per px of type. This tracks that envelope with ~9%
   slack so the guard never fires. Below 1051px the column is full width and the
   original size already fits, so that range is left alone. The short-desktop
   rule below carries its own size; min-height keeps the two disjoint. */
@media (min-width:1051px) and (min-height:761px){
  .product-title h2{font-size:clamp(34px,calc(5vw - 18px),62px)!important}
}
.product-actions{row-gap:14px}.product-actions a{min-height:42px;display:inline-flex;align-items:center}
.room-exit-button{min-width:184px;justify-content:center}
.room-motion-guide{max-width:calc(100vw - 28px)}
.icon-stage{isolation:isolate}
.icon-stage::after{content:"";position:absolute;z-index:-1;left:16%;right:16%;bottom:4px;height:15px;border-radius:50%;background:rgba(0,0,0,.5);filter:blur(8px)}
.icon-stage img{animation:iconSettle 4.5s ease-in-out infinite!important;transform-origin:50% 100%}
@keyframes iconSettle{0%,100%{transform:translateY(-2px) scale(1)}50%{transform:translateY(-2px) scale(1.018)}}

/* The final room acts as a deliberate exit, not an unfinished blank screen. */
.final-room::before{content:"LYVORIAN STUDIO";position:absolute;left:50%;top:50%;z-index:0;color:rgba(255,255,255,.025);font:600 clamp(76px,14vw,230px)/.8 Manrope,sans-serif;letter-spacing:-.08em;white-space:nowrap;transform:translate(-50%,-50%)}
.final-copy{padding:36px;border:1px solid rgba(255,255,255,.13);background:linear-gradient(135deg,rgba(43,45,49,.62),rgba(17,18,22,.46));backdrop-filter:blur(18px)}

/* The closing room now has someone in it, standing at an open door with a hand
   out. She occupies x 0.72-0.98 of the render, so the copy is pulled off centre
   to the left rather than sitting on top of her - the same left-copy /
   right-subject split the conference room uses. Every word of the panel stays;
   only its position changes. Below 1051px there is not enough width to hold both
   side by side, so the panel re-centres and the vignette carries the contrast. */
@media (min-width:1051px){
  .final-room .final-copy{margin-left:clamp(40px,6vw,110px);margin-right:auto;
    width:min(56vw,660px);text-align:left}
  .final-room .final-copy p{margin-left:0}
  .final-room .final-copy>div{justify-content:flex-start}
  /* The narrower column broke "The next room" across two lines: at 129.6px it
     needs 765px and the column gives 586. Sized to the measured fit envelope —
     ~4.4px of headline per px of type — with the cap at 96px because the panel
     stops growing at 660px while the viewport does not. */
  .final-room .final-copy h2{font-size:clamp(52px,6.4vw,96px)}
}
/* A `.final-room .room-vignette` block used to sit here, meaning to mirror the
   vignette for the desktop split -- dark left under the copy, clear right where
   she stands. It never once applied. The corridor rule near the bottom of this
   file,
     .tour-room:not(.product-room):not(.website-room):not(.product-wing-room)
       > .room-vignette
   is five classes to its two and always won, so what this room has actually
   been lit by, on every screen, is that corridor gradient. Removed rather than
   left in place: the desktop lighting is unchanged by deleting a rule that was
   already losing, and a dead block here sends the next reader after a gradient
   the page never renders. The portrait override below has to carry six classes
   for exactly this reason. */
/* The giant LYVORIAN STUDIO watermark sat on a flat panel; over a photograph it
   reads as dirt on the lens. */
.final-room::before{opacity:.45}

/* ── The closing room in portrait ──────────────────────────────────────────
   Everything above is the desktop split: copy left, her standing right. In
   portrait there is no second half of the frame to put a panel in -- she is
   centred and full height -- so the same panel landed on top of her. Measured
   on the real phone at 411x757, its top edge sat at source fraction .295,
   between her chin (y 181) and her shoulders (y 206): a 1px border ruled
   straight across her throat, which is what "it's overlapping, face is gone"
   describes. Smaller frames were worse -- .187 at 320x568 and .247 at 360x640
   put the edge across her head and her face.

   So in portrait the panel stops being a panel and the room carries the copy:
   she keeps the top of the frame in clear air, the words stand on the floor
   inside a fade that has no edge to cut with.

   13/10 is character-for-character the media query on the page's own <source>,
   so this block and the portrait asset switch on and off together. */
@media (max-width:1050px) and (max-aspect-ratio:13/10){

  /* Lift the crop off her head. object-fit:cover centres the slice, and the
     deeper the crop the more it eats from the top: at 768x1024 it removed her
     head completely (hair top at y -71) and on the phone it left her level
     with the header. Anchoring the slice near the top keeps her whole head in
     frame at every portrait ratio. Her shoes are what goes instead, and the
     copy stands over that part of the floor anyway. */
  .final-room .room-bg{object-position:50% 14%}

  /* The vignette is atmosphere here, never legibility: products.js:393 drives
     --vignette-opacity between .72 and 1 as a room takes focus, so anything
     load-bearing on this layer goes 28% weaker exactly while the room is
     arriving. Weight at the top for the glass header to sit on, a little
     pooling at the floor, and her face and hand left alone.

     The selector is the corridor rule further down this file with .final-room
     added, and it has to be. That rule --
       .tour-room:not(.product-room):not(.website-room):not(.product-wing-room)
         > .room-vignette
     -- is five classes, so a plain `.final-room .room-vignette` (two) loses to
     it no matter which media query it sits in. Verified on the device: the
     first attempt at this fix computed to the corridor's 90deg left scrim,
     not to what is written here. Six classes wins. */
  .tour-room.final-room:not(.product-room):not(.website-room):not(.product-wing-room)
    > .room-vignette{
    background:
      linear-gradient(180deg,rgba(8,9,12,.60),rgba(8,9,12,.18) 84px,transparent 152px),
      linear-gradient(0deg,rgba(8,9,12,.50),rgba(8,9,12,.14) 24%,transparent 44%);
  }

  /* Full-bleed and floor-anchored, with the scrim carried on the copy's OWN
     background. That is deliberate: .final-copy is opacity-animated through
     --content-opacity, so a scrim living here fades in step with the text and
     can never be weaker than the words it is holding up. No border, no inset
     highlight, no blur -- the shared glass block sets those with !important,
     and that bright top hairline was half of what did the cutting.

     The gradient's third stop is the same expression as padding-top, so the
     fade reaches full strength exactly where the first line of type begins,
     at any viewport height. */
  .final-room .final-copy{
    align-self:end;
    width:100%;
    max-width:none;
    margin:0;
    padding:clamp(92px,19svh,184px) 20px max(20px,env(safe-area-inset-bottom));
    text-align:center;
    border:0 !important;
    border-radius:0 !important;
    box-shadow:none !important;
    backdrop-filter:none !important;
    -webkit-backdrop-filter:none !important;
    background:linear-gradient(180deg,
      transparent 0,
      rgba(8,9,12,.40) clamp(54px,11svh,108px),
      rgba(8,9,12,.87) clamp(92px,19svh,184px),
      rgba(8,9,12,.95) 100%) !important;
  }

  /* Two lines at any width. "The next room" is the binding line; sized off the
     content box (100vw minus the 20px gutters, with slack) so it never takes
     the third line it takes today at every phone width from 320 to 480. */
  .final-room .final-copy h2{
    margin:14px 0 16px;
    font-size:min(72px,(100vw - 40px) / 6.2);
  }

  .final-room .final-copy p{
    max-width:40ch;
    margin-inline:auto;
    font-size:14px;
    line-height:1.55;
    color:#c3c5ca;
  }

  /* One row rather than a stack: the block has to be short enough to sit
     wholly below her, and both actions still clear 44px. */
  .final-room .final-copy>div{
    flex-direction:row;
    flex-wrap:wrap;
    justify-content:center;
    gap:12px 20px;
    margin-top:22px;
  }

  /* products.css draws "Return home" with a border-bottom, and the coarse
     pointer rule makes that link a 44px flex box -- so the line parks ~20px
     under a 10px label and reads as a stray rule, especially now the two
     actions sit side by side. The target stays 44px; the line moves onto the
     text. */
  .final-room .final-copy>div>a:not(.office-button){
    border-bottom-color:transparent;
    text-decoration:underline;
    text-decoration-thickness:1px;
    text-underline-offset:5px;
    text-decoration-color:rgba(255,255,255,.45);
  }

  /* Short frames -- a phone in landscape-ish portrait, a browser window with
     the keyboard up. Tighten rather than let the fade climb into her face. */
  @media (max-height:620px){
    .final-room .final-copy{padding-top:clamp(64px,13svh,108px)}
    .final-room .final-copy h2{margin:10px 0 12px;font-size:min(52px,(100vw - 40px) / 6.2)}
    .final-room .final-copy p{font-size:13px}
    .final-room .final-copy>div{margin-top:16px}
  }
}

/* ── The closing room in landscape, below the desktop breakpoint ───────────
   Above 13/10 the <picture> serves the landscape render, where she stands on
   the right -- so the left scrim above is the correct lighting and stays. What
   was missing is the desktop's left-aligned copy, which started at 1051px: on
   a 757x411 phone the centred panel sat on her, and its top edge was behind
   the fixed header (y 21-45 against a 78px header). */
@media (max-width:1050px) and (min-aspect-ratio:13/10){
  .final-room .final-copy{
    align-self:center;
    justify-self:start;
    width:min(60vw,540px);
    margin:0 auto 0 clamp(16px,5vw,52px);
    padding:20px 22px;
    text-align:left;
  }
  /* Sized off the column's real width, cap included -- 60vw alone overstates it
     once min() clamps at 540px, which is what pushed "The next room" onto a
     second line at 1024x768. "The next room" is the binding line at a measured
     5.9041px of width per px of type; 6.2 keeps 5% in hand. */
  .final-room .final-copy h2{
    margin:10px 0 12px;
    font-size:min(84px,(min(60vw,540px) - 44px) / 6.2);
  }
  .final-room .final-copy p{margin-left:0;max-width:44ch;font-size:13px;line-height:1.5}
  .final-room .final-copy>div{
    flex-direction:row;
    flex-wrap:wrap;
    justify-content:flex-start;
    gap:10px 18px;
    margin-top:16px;
  }
  .final-room .final-copy>div>a:not(.office-button){
    border-bottom-color:transparent;
    text-decoration:underline;
    text-decoration-thickness:1px;
    text-underline-offset:5px;
    text-decoration-color:rgba(255,255,255,.45);
  }

  /* A landscape phone is far shorter than a landscape tablet, and the emulated
     sizes I first tested (411, 390, 430 tall) were all too generous: the real
     S10+ reports 828x316 with Chrome's URL bar showing. At that height the copy
     is 299px in a 316px frame, so centring it put "Tour complete" ENTIRELY
     behind the 78px glass header and clipped the top of the headline. The
     portrait side already had a short-frame block, but it is nested in the
     portrait query, so landscape never saw it.

     Below 520px of height the block tightens and anchors to the floor of the
     frame, which is what guarantees it starts below the header rather than
     hoping it is short enough to. */
  @media (max-height:520px){
    .final-room .final-copy{
      align-self:end;
      margin-bottom:max(10px,env(safe-area-inset-bottom));
      padding:12px 18px;
    }
    .final-room .final-copy small{font-size:8px}
    .final-room .final-copy h2{
      margin:6px 0 8px;
      font-size:min(44px,(min(60vw,540px) - 36px) / 6.2);
    }
    .final-room .final-copy p{max-width:52ch;font-size:12px;line-height:1.45}
    .final-room .final-copy>div{gap:8px 14px;margin-top:10px}
  }
}

/* The project floor is a deliberate destination: the corridor exists only after entry. */
.projects-entry-panel{
  position:absolute;z-index:50;left:50%;top:50%;display:grid;width:min(760px,calc(100% - 48px));
  padding:clamp(28px,4vw,58px);overflow:hidden;border:1px solid rgba(255,255,255,.2);border-radius:28px;
  background:linear-gradient(135deg,rgba(50,52,58,.84),rgba(16,17,20,.72) 58%,rgba(25,25,34,.78));
  box-shadow:inset 0 1px rgba(255,255,255,.18),0 42px 110px rgba(0,0,0,.5),0 0 70px rgba(175,176,178,.08);
  backdrop-filter:blur(24px) saturate(1.22);transform:translate(-50%,-48%);transition:opacity .38s ease,visibility .38s,transform .65s var(--ease)
}
.projects-entry-panel::before{content:"";position:absolute;inset:0;pointer-events:none;background:radial-gradient(circle at 84% 18%,rgba(197,198,201,.15),transparent 27%),radial-gradient(circle at 8% 92%,rgba(175,176,178,.15),transparent 32%),linear-gradient(115deg,transparent 35%,rgba(255,255,255,.045) 50%,transparent 65%)}
.projects-entry-panel::after{content:"";position:absolute;right:clamp(22px,4vw,48px);top:0;width:1px;height:100%;background:linear-gradient(transparent,rgba(255,255,255,.2),transparent);opacity:.55}
.projects-entry-panel>*{position:relative;z-index:1}
.projects-entry-panel>small{display:flex;align-items:center;gap:10px;color:#e1e2e5;font-size:10px;font-weight:600;letter-spacing:.18em}
.projects-entry-panel>small i{width:8px;height:8px;border-radius:50%;background:var(--mint);box-shadow:0 0 18px rgba(197,198,201,.8)}
.projects-entry-panel h2{max-width:640px;margin:30px 0 20px;font-size:clamp(52px,6.2vw,88px);line-height:.78;letter-spacing:-.07em}
.projects-entry-panel h2 em{color:var(--lilac);font-family:var(--serif);font-weight:400}
.projects-entry-panel>p{max-width:590px;margin:0;color:#c4c6ca;font-size:13px;line-height:1.7}
.projects-entry-index{position:absolute;right:24px;top:18px;color:rgba(255,255,255,.045);font:600 clamp(96px,14vw,170px)/1 var(--sans);letter-spacing:-.09em}
.projects-entry-button{display:flex;align-items:center;justify-content:space-between;gap:28px;width:min(100%,470px);min-height:76px;margin-top:34px;padding:10px 12px 10px 22px;color:#f5f4ef;border:1px solid rgba(255,255,255,.22);border-radius:18px;background:linear-gradient(135deg,rgba(255,255,255,.13),rgba(255,255,255,.045));box-shadow:inset 0 1px rgba(255,255,255,.17),0 18px 45px rgba(0,0,0,.25);backdrop-filter:blur(18px);cursor:pointer;text-align:left;transition:transform .35s var(--ease),border-color .35s,box-shadow .35s}
.projects-entry-button>span{display:grid;gap:5px}.projects-entry-button small{color:#9fa2a8;font-size:8px;letter-spacing:.14em;text-transform:uppercase}.projects-entry-button b{font-size:15px;letter-spacing:-.02em}.projects-entry-button>i{display:grid;place-items:center;width:50px;height:50px;color:#17181b;border-radius:14px;background:linear-gradient(135deg,var(--mint),#e2e3e6);font-size:18px;font-style:normal;box-shadow:0 10px 30px rgba(197,198,201,.22);transition:transform .35s var(--ease)}
.projects-entry-button:hover,.projects-entry-button:focus-visible{border-color:rgba(197,198,201,.66);box-shadow:inset 0 1px rgba(255,255,255,.2),0 24px 58px rgba(0,0,0,.34),0 0 34px rgba(197,198,201,.1);transform:translateY(-3px)}
.projects-entry-button:hover>i,.projects-entry-button:focus-visible>i{transform:translate(2px,-2px)}
.projects-entry-meta{display:flex;flex-wrap:wrap;gap:9px;margin-top:24px}.projects-entry-meta span{padding:7px 10px;color:#9da0a6;border:1px solid rgba(255,255,255,.11);border-radius:999px;background:rgba(12,13,16,.28);font-size:8px;letter-spacing:.09em;text-transform:uppercase}
.product-wing-room .corridor-map{opacity:0;visibility:hidden;pointer-events:none;transform:translate3d(-50px,var(--content-shift,0),var(--content-z,0)) scale(.97);transition:opacity .38s ease,visibility .38s,transform .7s var(--ease)}
.product-wing-room.projects-open .projects-entry-panel{opacity:0;visibility:hidden;pointer-events:none;transform:translate(-50%,-52%) scale(.96)}
.product-wing-room.projects-open .corridor-map{opacity:1;visibility:visible;pointer-events:auto;transform:translate3d(var(--content-x,0),var(--content-shift,0),var(--content-z,0)) rotateY(var(--content-turn,0))}
.product-wing-room:has(.corridor-map:target) .projects-entry-panel{opacity:0;visibility:hidden;pointer-events:none}
.product-wing-room .corridor-map:target{opacity:1;visibility:visible;pointer-events:auto}
.corridor-heading{position:relative}.corridor-heading p{padding-top:38px}
.projects-close-button{position:absolute;right:0;top:0;min-height:30px;padding:0 11px;color:#d9dadc;border:1px solid rgba(255,255,255,.18);border-radius:999px;background:rgba(255,255,255,.06);cursor:pointer;font-size:8px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;transition:color .25s,border-color .25s,background .25s}
.projects-close-button:hover,.projects-close-button:focus-visible{color:#17181b;border-color:var(--mint);background:var(--mint)}

@media (min-width:1051px) and (max-height:760px){
  .product-room .product-suite,.website-room .website-suite-room{top:80px!important;height:calc(100svh - 92px)!important;margin:80px auto 12px!important}
  .product-story,.website-copy{padding:24px 30px!important}
  .product-title{margin-top:24px!important}.product-title h2{font-size:clamp(38px,4vw,60px)!important}
  .product-story>p{margin-top:18px!important;line-height:1.5}.product-story ul{margin:18px 0!important}
  .website-copy>small{margin-top:18px!important}.website-copy h2{font-size:clamp(40px,4.5vw,66px)!important}
}

@media (max-width:1050px){
  .product-story,.website-copy{overflow:visible}
  .final-room{min-height:100svh!important}
  .projects-entry-panel{top:50%;width:min(720px,calc(100% - 40px))}
}

@media (max-width:720px){
  .room-exit-button{top:73px;left:12px;min-width:0;max-width:calc(100vw - 24px);padding:0 12px}
  .room-motion-guide{left:12px!important;right:12px!important;bottom:max(10px,env(safe-area-inset-bottom))!important}
  .product-title{align-items:flex-start;gap:14px}.product-title h2{font-size:clamp(34px,11vw,48px)!important}
  .product-actions{align-items:flex-start;flex-direction:column;gap:10px}
  .office-corridor{overflow-x:hidden!important}
  .final-copy{width:calc(100% - 28px);padding:28px 20px}.final-copy h2{font-size:clamp(54px,18vw,78px)}
  .product-wing-room:not(.projects-open){min-height:100svh!important}
  .projects-entry-panel{position:absolute;top:50%;width:calc(100% - 28px);padding:28px 20px;border-radius:20px;transform:translate(-50%,-47%)}
  .projects-entry-panel h2{margin:24px 0 16px;font-size:clamp(46px,15vw,68px)}
  .projects-entry-panel>p{font-size:12px}.projects-entry-index{right:18px;font-size:92px}
  .projects-entry-button{min-height:70px;margin-top:26px;padding-left:17px}.projects-entry-button>i{width:46px;height:46px}
  .projects-entry-meta{margin-top:18px}.projects-entry-meta span{font-size:7px}
  .product-wing-room .corridor-map{display:none;opacity:0;visibility:hidden}
  .product-wing-room.projects-open .corridor-map{display:grid;opacity:1;visibility:visible;transform:none}
  .corridor-heading p{padding-top:0}.projects-close-button{position:static;grid-column:1;width:max-content;margin-top:8px}
}

@media (prefers-reduced-motion:reduce){
  .icon-stage img{animation:none!important;transform:none!important}
  .product-room,.website-room{scroll-behavior:auto!important}
  .projects-entry-panel,.product-wing-room .corridor-map{transition:none!important}
}

/* V18 — Project floor becomes an editorial suite directory, not five empty panels. */
.product-wing-room.projects-open .corridor-map{top:calc(98px + 10svh);bottom:auto;height:min(760px,calc(100svh - 130px));padding:22px 24px 24px!important;background:linear-gradient(145deg,rgba(35,36,42,.92),rgba(14,15,18,.88));border-color:rgba(255,255,255,.16);box-shadow:inset 0 1px rgba(255,255,255,.12),0 34px 100px rgba(0,0,0,.46);backdrop-filter:blur(24px) saturate(1.2);transform:none!important}
.corridor-heading{grid-template-columns:minmax(0,1fr) minmax(260px,380px);padding:0 2px 17px}
.corridor-heading>span{font-size:9px}.corridor-heading h2{font-size:clamp(34px,3.2vw,48px);letter-spacing:-.055em}.corridor-heading p{max-width:350px;padding-top:34px;color:#aeb0b6;font-size:10px}
.office-corridor{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));grid-template-rows:repeat(2,minmax(170px,1fr));gap:12px;min-height:0;padding:14px 0 0;border-top:1px solid rgba(255,255,255,.13);background:transparent!important}
.office-corridor::before{inset:14px 0 0;height:auto;clip-path:none;background:linear-gradient(rgba(255,255,255,.022) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,.022) 1px,transparent 1px);background-size:32px 32px;mask-image:linear-gradient(#000,transparent);opacity:.55}
.room-door{min-height:0;padding:0;isolation:isolate;border:1px solid rgba(255,255,255,.12)!important;border-radius:16px!important;background:linear-gradient(145deg,rgba(50,51,58,.78),rgba(24,25,30,.94))!important;box-shadow:inset 0 1px rgba(255,255,255,.1),0 18px 42px rgba(0,0,0,.2)!important;transform:none!important;transition:transform .45s var(--ease),border-color .3s,box-shadow .3s!important}
.room-door:nth-child(1){grid-column:1/5;grid-row:1/3;--door-accent:114,224,196}
.room-door:nth-child(2){grid-column:5/9;grid-row:1;--door-accent:143,228,178}
.room-door:nth-child(3){grid-column:5/9;grid-row:2;--door-accent:183,166,255}
.room-door:nth-child(4){grid-column:9/13;grid-row:1;--door-accent:244,183,118}
.room-door:nth-child(5){grid-column:9/13;grid-row:2;--door-accent:171,153,255}
.room-door::before{inset:0!important;border:0!important;border-radius:inherit!important;background:radial-gradient(circle at 17% 34%,rgba(var(--door-accent),.19),transparent 30%),linear-gradient(115deg,rgba(255,255,255,.055),transparent 34%,transparent 70%,rgba(var(--door-accent),.055))!important;transition:opacity .35s}
.room-door::after{left:18px;right:18px;bottom:0;height:2px;background:linear-gradient(90deg,transparent,rgba(var(--door-accent),.9),transparent);filter:none;opacity:.65}
.room-door>span{left:20px;top:18px;color:rgb(var(--door-accent));font-size:8px;letter-spacing:.15em}
.room-door>img,.website-door-mark{left:24%;top:48%;width:66px;height:66px;border:1px solid rgba(255,255,255,.13);border-radius:15px!important;background:rgba(10,11,14,.38);box-shadow:0 18px 38px rgba(0,0,0,.34),0 0 28px rgba(var(--door-accent),.09);transform:translate(-50%,-50%)!important}
.room-door>b{position:absolute;z-index:3;left:39%;right:18px;top:28%;font-size:clamp(21px,2vw,30px);line-height:.92;letter-spacing:-.055em}
.room-door>p{position:absolute;z-index:3;left:39%;right:22px;top:48%;margin:0;color:#aeb0b6;font-size:9px;line-height:1.5}
.room-door>i{position:absolute;z-index:3;left:39%;bottom:51px;margin:0;padding:6px 8px;color:rgb(var(--door-accent));border:1px solid rgba(var(--door-accent),.22);border-radius:999px;background:rgba(var(--door-accent),.07);font-size:7px;font-weight:600}
.room-door>em{position:absolute;z-index:3;left:20px;right:20px;bottom:17px;display:flex;align-items:center;justify-content:space-between;margin:0;padding:11px 0 0;color:#d8d9dc;border-top:1px solid rgba(255,255,255,.11);font-size:8px;text-transform:uppercase;letter-spacing:.08em}
.room-door>em u{text-decoration:none;color:rgb(var(--door-accent));font-size:13px;transition:transform .3s var(--ease)}
.room-door:hover,.room-door:focus-visible{border-color:rgba(var(--door-accent),.52)!important;box-shadow:inset 0 1px rgba(255,255,255,.13),0 28px 58px rgba(0,0,0,.34),0 0 30px rgba(var(--door-accent),.08)!important;transform:translateY(-5px)!important}
.room-door:hover::before,.room-door:focus-visible::before{opacity:1.35}.room-door:hover>em u,.room-door:focus-visible>em u{transform:translate(2px,-2px)}
.room-door-featured>span{left:28px;top:24px}.room-door-featured>img{left:28px;top:84px;width:82px;height:82px;transform:none!important}
.room-door-featured>b{left:28px;right:25px;top:auto;bottom:151px;font-size:clamp(34px,3.6vw,57px)}
.room-door-featured>p{left:28px;right:28px;top:auto;bottom:99px;max-width:310px;font-size:10px}
.room-door-featured>i{left:28px;bottom:58px}.room-door-featured>em{left:28px;right:28px;bottom:18px}
.room-door-featured::before{background:radial-gradient(circle at 22% 24%,rgba(var(--door-accent),.22),transparent 32%),linear-gradient(155deg,rgba(255,255,255,.055),transparent 40%),repeating-linear-gradient(90deg,transparent 0 47px,rgba(255,255,255,.018) 48px)!important}
.website-door-mark{display:flex;padding:13px}.website-door-mark u{width:12px;background:linear-gradient(rgb(var(--door-accent)),var(--mint))}

@media (max-width:1050px) and (min-width:721px){
  .product-wing-room.projects-open{min-height:100svh!important}
  .product-wing-room.projects-open .corridor-map{inset:calc(88px + 10svh) 3% auto;height:calc(100svh - 110px)}
  /* 145px was below what a card can actually hold. Its icon and title are
     anchored to the top and its badge and CTA to the bottom, so as the row
     shrinks the two halves close on each other: at a 700px-tall window every one
     of the five cards had the badge sitting in the description, and the featured
     card had it in the title. The content needs ~200px.

     The row minimum is the whole fix. .office-corridor is already overflow-y:auto,
     so once the rows are allowed their real height the grid simply scrolls on a
     short window, which is the honest outcome — a visitor scrolls a little
     instead of reading two lines printed on top of each other. */
  .office-corridor{grid-template-columns:repeat(2,minmax(0,1fr));grid-template-rows:repeat(3,minmax(200px,1fr));gap:9px}
  .room-door:nth-child(1){grid-column:1/3;grid-row:1}.room-door:nth-child(2),.room-door:nth-child(3),.room-door:nth-child(4),.room-door:nth-child(5){grid-column:auto;grid-row:auto}
  /* Same two-line title, same collision, smaller: p sat at top:92px, only 40px
     below a title that starts at 52px and stands 54px tall, so the description
     rendered inside the title's own box. 108px clears the title by 3px while
     staying above the badge, which is bottom-anchored and therefore rises as the
     card shrinks — 118px cleared the title but collided with the badge once the
     card came down to 187px at 780px wide. */
  .room-door-featured>img{left:22px;top:58px;width:64px;height:64px}.room-door-featured>b{left:105px;top:52px;bottom:auto;font-size:30px}.room-door-featured>p{left:105px;top:108px;bottom:auto}.room-door-featured>i{left:105px;bottom:45px}
  .room-door>p{font-size:8px}.room-door>em{bottom:12px;padding-top:8px}
}

@media (max-width:720px){
  /* 1900px was sized for the old five-card stack. With two wings the corridor is
     621px, so the room stayed 1900px tall and the map sat at y 966 -- a phone
     visitor tapped "Go to Projects Room" and saw only the corridor render, with
     the wings 200px below the fold. min-height lets it grow again for the apps
     pane, which is taller. */
  .product-wing-room.projects-open{min-height:100svh!important}
  .product-wing-room.projects-open .corridor-map{width:calc(100% - 24px)!important;height:auto;margin:78px auto 18px;padding:18px 14px!important;border-radius:18px!important}
  .corridor-heading{grid-template-columns:1fr;padding-bottom:16px}.corridor-heading p{grid-column:1;padding-top:0}.projects-close-button{margin-top:12px}
  .office-corridor{grid-template-columns:1fr;grid-template-rows:auto;gap:10px;padding-top:12px}
  .room-door:nth-child(1),.room-door:nth-child(2),.room-door:nth-child(3),.room-door:nth-child(4),.room-door:nth-child(5){grid-column:1;grid-row:auto;min-height:260px}
  .room-door:nth-child(1){min-height:340px}
  .room-door>span{left:17px;top:16px}.room-door>img,.website-door-mark{left:22%;top:47%;width:62px;height:62px}
  .room-door>b{left:38%;top:27%;font-size:25px}.room-door>p{left:38%;top:47%;font-size:9px}.room-door>i{left:38%;bottom:49px}.room-door>em{left:17px;right:17px;bottom:15px}
  /* The featured card's title carries an authored break -- "Finance<br>Tracker"
     in project-office.html -- so it is ALWAYS two lines, never one that happens
     to wrap. Anchored from the BOTTOM, those two lines grew upward into the app
     icon and left the description sitting above the title's own lower edge: 29px
     of overlap on the icon and 35px on the copy, identical at 320, 412, 560 and
     720 because nothing here depends on the width.

     Anchored from the TOP instead, every element starts below the one before it,
     so the stack cannot collide however tall the title becomes. bottom:auto is
     doing real work on both -- the base rule above sets a bottom, and an
     absolutely positioned box given both top and bottom is stretched between
     them rather than sized to its content. */
  .room-door-featured>img{left:20px;top:44px;width:64px;height:64px}
  .room-door-featured>b{left:20px;right:20px;top:120px;bottom:auto;font-size:34px}
  .room-door-featured>p{left:20px;right:20px;top:200px;bottom:auto;font-size:10px}
  .room-door-featured>i{left:20px;bottom:53px}
  .room-door-featured>em{left:20px;right:20px;bottom:15px}
}

/* ── touch target sizes ─────────────────────────────────────────────
   The room rail rendered as 8px dots on a phone: visible, but far below the
   44px minimum, so the seven of them were effectively untappable — the same
   failure the main site's nav had.

   Padding the dot alone does not fix it. At an 11px dot on a 9px gap the
   expanded hit areas would overlap and the later sibling would swallow the
   earlier one's taps, so the pitch has to grow with the target. Widening the
   gap to 34px gives each dot a clean 45px slot; seven of those is 281px, which
   still fits a short viewport.

   Scoped to coarse pointers: a mouse is precise, the dots carry hover labels,
   and the desktop rail's rhythm is deliberate. */
@media (pointer: coarse) {
  /* The wordmark is the way back out; 15px of text is not a target. */
  .tour-brand, .exit-link { min-height: 44px; align-content: center; }
  .splash [data-splash-skip] { min-height: 44px; padding-inline: 14px; }

  /* Real controls, which sit at 41px and 15px without this. The exemption that
     used to be noted here was for the decorative buttons inside .web-lab; that
     block no longer exists, so the note went with it. */
  .office-button,
  .product-actions a,
  .final-copy div a { min-height: 44px; display: inline-flex; align-items: center; }
}

/* ── theme ──────────────────────────────────────────────────────────
   This page does not follow the site's light/dark toggle, and that is a
   decision rather than an oversight: every room is a dark photographic
   render, so a light mode here would need new artwork, not new CSS — a
   light-themed chrome wrapped around night-time rooms reads worse than a
   page that simply commits.

   What was missing is the declaration. Without color-scheme the browser
   still paints form controls, scrollbars and the overscroll gutter from the
   visitor's OS preference, so a light-mode visitor got light scrollbars
   against a near-black building. */
:root { color-scheme: dark; }

/* Off-screen but still announced — not display:none, which would remove it
   from the accessibility tree along with everything else. */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ── splash mark ────────────────────────────────────────────────────
   The orbit used to hold a letter monogram, <b>LY</b>, styled twice —
   a gradient rounded square in products.css and then a serif "LY" with a
   stacked text-shadow in product-premium.css, the second overriding the first
   through fourteen !important declarations. Swapping the element to an <img>
   drops every one of those rules on the floor, because they all select `b`,
   which is why this block can be plain CSS with no !important of its own.
   The mark is 332x416, so only the width is set and the height follows. */
.splash-orbit .splash-mark{
  position:absolute; z-index:4; left:50%; top:50%;
  width:88px; height:auto;
  transform:translate(-50%,-50%);
  filter:drop-shadow(0 10px 24px rgba(0,0,0,.78));
}
@media (max-width:430px){
  .splash-orbit .splash-mark{ width:56px; }
}

/* ── room pacing ────────────────────────────────────────────────────
   The homepage hero gives each of its eight transitions 150vh — descent.css:45
   records that 520vh was tried first and "read as hurried". The Project Office
   was giving each room exactly 100svh, so rooms arrived 1.5x faster than hero
   frames while carrying far more movement per pixel: a full camera push, pan,
   X/Z translation, rotation and opacity fade rather than a crossfade.

   Above 1050px the rooms are a sticky stack — position:sticky, top:0,
   height:100svh — so each contributes exactly its own height in scroll and
   making it taller would give it flow it cannot pin within. The margin does the
   job instead: the room's box stays 100svh and stays pinned, while the NEXT
   room's arrival is pushed 100svh later. Stride goes to 200svh with the pin
   untouched, and the room rests on screen for the whole middle screen of it.

   Below 1050px the lever is different, and so is what it buys. products.css
   overrides the sticky rule with position:relative under (max-width:1050px), so
   nothing pins there: a room simply scrolls past and its copy always travels at
   exactly the scroll rate — measured, the copy's centre runs monotonically from
   2.28vh to -0.28vh with no still point anywhere. So min-height cannot buy
   stillness here, only distance between arrivals.

   It stays at 150svh and is deliberately NOT matched to the desktop stride. The
   copy is not centred in its room; it sits about two thirds down the box, so
   inflating the box pushes it further past the window the rest curve freezes.
   Measured on room 2 at 375x812, sampling the room's whole span: with the copy
   on screen, the camera's X swing is 123.7px at 100svh, 95.3px at 150svh and
   91.4px at 200svh — but the share of the rest window in which the copy is
   visible at all goes 0.67 at 150svh to 0.00 at 200svh, where the freeze lands
   entirely on empty backdrop two screens below the copy. The last 3.9px of
   quiet is not worth spending the rest on nothing, plus 2.5 more screens of
   scrolling. The product and website rooms are left alone — they already run to
   1450px and 1320px because they hold a whole product panel.

   Two rooms are deliberately NOT slowed, on either layout. .product-wing-room is
   a decision point holding a button, and .final-room is the way out — neither is
   a room you walk through, and lingering in them reads as waiting rather than as
   pacing. Both were already pinned to one screen below 1050px by !important
   rules further up this file; naming them here makes that intent explicit
   instead of leaving it to depend on someone else's !important winning. */
/* Complementary to products.css's own breakpoint, not merely adjacent to it.
   products.css turns the sticky stack off at (max-width:1050px); this rule has
   to apply exactly when that one does NOT. Written as (min-width:1051px) it left
   a gap: a viewport strictly between 1050 and 1051 CSS px — which browser zoom
   produces routinely, 1313 device px at 125% is 1050.4 — still got the pinned
   desktop layout but matched neither pacing rule, so it had no gap at all.
   Stride collapses to 100svh there, restStart and restEnd both resolve to .5,
   and the plateau this block exists to create has zero width: every room is
   permanently either ramping in or ramping out. `not all and (...)` is the exact
   complement by construction, so no width can fall between the two again. */
@media not all and (max-width:1050px){
  /* 100svh, not 50. The gap is not merely spacing — it IS the plateau, the only
     stretch in which a room is pinned, uncovered and simply on screen. A room's
     own box is 100svh so that it can pin at all, and the wipe across that box is
     layout-driven: the next room physically slides up over this one in exactly
     one viewport and no amount of JS can slow it. Only the margin buys stillness.
     At 100svh the three segments — arrive, rest, be covered — come out equal, so
     the rest is a full screen and the phase plateau lands on [1/3, 2/3], which is
     measured to be exactly where it lands. Stride 200svh, document 1200svh. */
  .tour-room:not(.product-wing-room):not(.final-room){ margin-bottom:100svh; }
}
@media (max-width:1050px){
  .tour-room:not(.product-room):not(.website-room):not(.product-wing-room):not(.final-room){
    min-height:150svh;
  }
}
/* Neither of the two rules above may survive reduced motion. products.css:93
   drops the sticky stack back to position:relative at every width, so the gap
   stops being scroll a pinned room sits through and becomes a literal empty band
   of page background. Measured on the live site at 1280x900: with the stack
   sticky, the middle of a gap hits room#0, still pinned; with it relative, the
   same points hit MAIN. At the current 100svh that is five full screens of bare
   background, shown to the visitors the setting exists to protect.

   Careful about WHY it buys nothing, because the obvious reading is wrong:
   updateRoomCamera is NOT skipped under reduced motion. The only
   `if (reduceMotion) return;` in products.js sits in the pointermove handler; the
   camera keeps running and keeps writing every custom property. What suppresses
   it is CSS — products.css:93 forces transform:none!important and
   opacity:1!important on .room-copy and its siblings, so the writes land on
   properties nothing reads. The scroll distance is therefore paying for motion
   that is computed and then thrown away. Do NOT "make the comment true" by
   returning early from updateRoomCamera: setActiveRoom(currentRoom) is called at
   the END of it, so an early return freezes the header room number, the room name
   and the rail's aria-current — an accessibility regression aimed straight at
   these same visitors.

   The mobile branch has no void — an inflated room paints its own box — but it is
   dead scroll pacing motion that is being discarded, so it goes back too.
   Both selectors mirror their rule above exactly, so this reverts what the
   pacing added and nothing else; equal specificity and later in source order, so
   each wins over its own counterpart. It is not width-scoped, so it also covers
   the sub-pixel band the complement above now claims. products.js measures real
   offsetTops rather than assuming a stride, so the camera needs no matching
   change. */
@media (prefers-reduced-motion:reduce){
  .tour-room:not(.product-wing-room):not(.final-room){ margin-bottom:0; }
  .tour-room:not(.product-room):not(.website-room):not(.product-wing-room):not(.final-room){
    min-height:100svh;
  }
}


/* ══ REDESIGN PROTOTYPE · image-first rooms · room 06 only ═══════════════════
   The entry panel was a 760px card at 84% opacity, centred over the room render
   behind a 17px backdrop blur. That is why the artwork was never visible, and
   why commissioning new room images could not pay for itself: whatever we
   generated ended up behind an opaque box covering most of the frame.
   Here the card dissolves into a bottom band. The render reads across the top
   two thirds; the gradient only darkens where type actually sits. Type scale,
   the sans + Cormorant-italic pairing, the CTA and the tag pills are untouched
   -- this changes where the copy lives, not how it looks.
   Scoped to .product-wing-room so the other eleven rooms are unaffected until
   this pattern is judged. Delete this block to revert. */

.product-wing-room > .room-vignette{
  background:linear-gradient(180deg,
    rgba(12,13,16,.44) 0%,
    rgba(12,13,16,.10) 24%,
    rgba(12,13,16,.56) 60%,
    rgba(12,13,16,.94) 100%);
}

.product-wing-room .projects-entry-panel{
  left:0; right:0; top:auto; bottom:0;
  width:auto; max-width:none;
  padding:clamp(30px,4vw,58px) clamp(22px,7vw,118px) clamp(34px,5vw,70px);
  overflow:visible;
  border:0; border-radius:0;
  background:none;
  box-shadow:none;
  backdrop-filter:none; -webkit-backdrop-filter:none;
  transform:none;
}
/* the corner glow and the hairline were part of the card, not the content */
.product-wing-room .projects-entry-panel::before,
.product-wing-room .projects-entry-panel::after{display:none}

/* keep the exit choreography, minus the centring offset it used to assume */
.product-wing-room.projects-open .projects-entry-panel{
  transform:translateY(18px) scale(.985);
}

/* the numeral moves out of the card and sits over the render */
.product-wing-room .projects-entry-index{
  top:auto; bottom:calc(100% - 26px); right:clamp(20px,5vw,60px);
  color:rgba(255,255,255,.07);
}

@media (max-width:720px){
  .product-wing-room .projects-entry-panel{
    top:auto; bottom:0; width:auto;
    padding:24px 20px 28px;
    border-radius:0; transform:none;
  }
  .product-wing-room .projects-entry-index{bottom:calc(100% - 10px); right:18px}
}


/* ══ REDESIGN ROLLOUT · image-first, remaining rooms ═════════════════════════
   Room 06 above proved the pattern. The product and website rooms hid their
   render behind THREE stacked layers, not one: an 82%-opaque suite card with
   an 18px backdrop blur, a vignette running .88/.56/.72, and brightness(.72)
   on the image itself. Multiplied out, roughly 8% of the render survived to
   the screen, which is why nobody could see room art we had paid to generate.
   The suite card keeps a job here that the entry panel did not have -- it
   carries a two-column layout with real product screenshots -- so it stays,
   but as frosted glass rather than a wall. The 18px blur means what shows
   through is a soft wash, not competing detail. */

/* Descendant, NOT child: .room-bg lives inside a <picture>, so `.product-room >
   .room-bg` matches nothing. products.css has the same latent bug -- its
   `.product-room>.room-bg{filter:brightness(.62)}` has never applied, which is
   why the computed value was always .72 from the descendant rule above it. */
.product-room .room-bg,
.website-room .room-bg{
  filter:brightness(.92) saturate(.96) !important;
}

.product-room .room-vignette.strong,
.website-room .room-vignette.strong{
  background:
    radial-gradient(circle at 72% 27%,rgba(var(--suite-accent),.20),transparent 28%),
    radial-gradient(circle at 22% 78%,rgba(var(--suite-secondary),.13),transparent 33%),
    linear-gradient(90deg,rgba(10,11,13,.60),rgba(10,11,13,.24) 48%,rgba(10,11,13,.46));
}

.product-suite,
.website-suite-room{
  background:rgba(22,23,27,.66);
  box-shadow:0 35px 110px rgba(0,0,0,.5);
}

/* The corridor rooms were never the problem -- .room-copy is bare copy over a
   side gradient, not a card -- but the same gradient still buries the right
   third. Easing it lets the render carry the room without touching the type. */
.tour-room:not(.product-room):not(.website-room):not(.product-wing-room) > .room-vignette{
  background:
    linear-gradient(90deg,rgba(10,11,13,.90) 0%,rgba(10,11,13,.52) 42%,rgba(10,11,13,.06) 72%),
    linear-gradient(0deg,rgba(10,11,13,.68),transparent 48%);
}


/* On a phone the suite card is full-width and taller than the viewport, so its
   opacity is irrelevant -- there is simply no room left around it for the
   render to show. But .room-bg is position:fixed inside an open product room,
   so starting the card below the fold turns the scroll into a reveal: the
   render holds the top of the screen and the card rises over it. At the
   inherited margin-top:10svh it buried the image immediately. */
@media (max-width:720px){
  /* product-motion.css pins this card with `position:sticky!important; top:66px`
     and `margin:66px auto 0!important`, so a plain longhand loses to the
     important shorthand -- and even winning it would change nothing, because a
     card stuck 66px from the top leaves no room to reveal. Moving the sticky
     anchor itself is what works: the render holds the top 40% of the screen and
     the card pins beneath it. Needs !important purely to answer motion's. */
  .product-room .product-suite{
    top:40svh !important;
    margin-top:40svh !important;
  }
}


/* ══ GLASS · every panel in the office ══════════════════════════════════════
   Clear glass, not frosted. The first attempt used blur(34px), which reads as
   light coming through but hides WHAT is behind -- the room dissolved into a
   smooth wash. Real glass shows the scene through it, so the blur drops to 3px
   (just enough to feel like a pane rather than a hole) and the fill drops to a
   tint rather than a screen.

   The legibility then has to come from the TYPE, not from hiding the room
   behind it: everything inside these panels gets a tight dark shadow, which
   holds the glyph edges against whatever the render happens to be doing without
   putting a grey sheet over the view.

   .room-copy is excluded on purpose -- on desktop it is bare copy over the
   render with no panel at all, and glass there would invent a box. */
.tour-header,.room-sign,.speech,.manager-label,.wing-map,
.corridor-map,.product-suite,.website-suite-room,.product-story,.website-copy,
.final-copy,.room-metric,.room-door,.room-motion-guide,.room-exit-button{
  border-color:rgba(255,255,255,.42) !important;
  background:linear-gradient(135deg,
    rgba(120,124,136,.16),
    rgba(18,19,24,.13) 58%,
    rgba(10,11,15,.10)) !important;
  box-shadow:
    inset 0 1px rgba(255,255,255,.34),
    inset 0 -1px rgba(255,255,255,.09),
    0 18px 50px rgba(0,0,0,.30) !important;
  backdrop-filter:blur(3px) saturate(1.12) !important;
  -webkit-backdrop-filter:blur(3px) saturate(1.12) !important;
}

/* With the panel now see-through, the glyphs carry their own legibility. A
   tight dark shadow rather than a glow: it holds the letter edges over a bright
   patch of render without reading as a halo. */
.room-sign,.speech,.manager-label,.wing-map,.corridor-map,
.product-story,.website-copy,.final-copy,.room-metric,
.room-door,.room-motion-guide,.room-exit-button{
  text-shadow:0 1px 2px rgba(0,0,0,.85),0 2px 8px rgba(0,0,0,.55);
}

/* The exit button lights up on hover/focus: mint fill, dark #17181b label. The
   glass background above is !important, so it outranked that fill while the
   label still went dark - a focused "Back to project corridor" rendered
   near-black on near-black, which is how it looked on the phone. Re-assert the
   lit state. It is the one panel that deliberately stops being glass, because
   the state has to be unmistakable to a thumb. */
.room-exit-button:hover,.room-exit-button:focus-visible{
  background:var(--mint) !important;
  border-color:var(--mint) !important;
  text-shadow:none !important;
}

/* The conference room seats four people across the right of the frame, and the
   team speech bubble is 0.134 of the image wide while the widest gap between
   their faces is about 0.09 - so at bottom:47% it always covered somebody's
   face, and no crop of the render could avoid it. Dropping it to 37% on desktop
   puts it on the torso and table line beneath the group: bubble y 0.50-0.60
   against faces that end at 0.46. It stays right of the copy column (x 0.65-0.80
   vs the column's 0.26-0.61), so nothing else moves. The phone portrait is a
   different crop whose faces already sit above the bubble, so it is left alone. */
@media (min-width:1051px){
  .speech-team{bottom:37%}
}

/* Pills inside a see-through panel need a touch more body than the panel. */
.room-tags span,.product-story li,.projects-entry-meta span,.pchip{
  background:rgba(255,255,255,.10);
  border-color:rgba(255,255,255,.26);
}

@media (max-width:720px){
  .room-copy,.room-copy.copy-right,.room-copy.copy-bottom{
    background:linear-gradient(135deg,rgba(120,124,136,.18),rgba(18,19,24,.15) 58%,rgba(10,11,15,.12));
    backdrop-filter:blur(3px) saturate(1.12);
    -webkit-backdrop-filter:blur(3px) saturate(1.12);
    border:1px solid rgba(255,255,255,.36);
    text-shadow:0 1px 2px rgba(0,0,0,.85),0 2px 8px rgba(0,0,0,.55);
  }
}

/* ── Phone memory: stop the tour holding twelve full-screen renders at once ──
   Reported as the page crashing and glitching on a phone. Not reproduced in
   headless -- a desktop renderer has memory to spare and layerises differently
   -- so this targets the resource pressure rather than a captured stack.

   The measurable part: the twelve portrait renders decode to ~124MB of RGBA,
   roughly double what they did before the resolution pass, and products.css
   pins every one of them with `will-change:transform,filter` on
   `.tour-room .room-bg` -- unconditionally, not just the active room. A
   permanently promoted layer is a decode the browser will not discard, so all
   twelve stay resident whether or not they are on screen. On top of that the
   glass treatment puts backdrop-filter on ~36 elements across the tour, which
   is the most expensive thing a mobile GPU is asked to do here.

   Scoped to <=1050px. Desktop has the headroom and is left exactly as it is. */
@media (max-width:1050px){

  /* 1 - Let the browser skip and release rooms that are off screen. `auto` on
     contain-intrinsic-size means each room remembers its real height once
     rendered, so scroll position does not jump. Product rooms are excluded:
     they are position:fixed overlays, not part of the scroll flow. */
  .tour-room:not(.product-room):not(.website-room){
    content-visibility:auto;
    contain-intrinsic-size:auto 100svh;
  }

  /* 2 - Promote only the room being looked at. This is the pin: `will-change`
     on all twelve backgrounds tells the compositor to keep all twelve ready
     forever. The active room still gets the hint, so the camera move stays
     smooth where it is actually visible. */
  .tour-room .room-bg{will-change:auto}
  .tour-room.is-active .room-bg{will-change:transform}
  .room-copy,.product-suite,.website-suite-room,.wing-map,.final-copy{will-change:auto}
  .tour-room.is-active .room-copy,
  .tour-room.is-active .final-copy{will-change:transform,opacity}

  /* 3 - Drop backdrop-filter on phones. It is decorative here (a 3px blur) and
     it is the single most expensive effect on the page; the panels keep their
     look by carrying a little more of their own background instead, which is
     also what makes the text legible without the blur behind it. Contrast was
     re-measured on the composited frame after this change. */
  .tour-header,.room-sign,.speech,.manager-label,.wing-map,
  .corridor-map,.product-suite,.website-suite-room,.product-story,.website-copy,
  .final-copy,.room-metric,.room-door,.room-motion-guide,.room-exit-button,
  .projects-entry-panel{
    backdrop-filter:none !important;
    -webkit-backdrop-filter:none !important;
  }
  .tour-header,.room-sign,.speech,.manager-label,.wing-map,
  .corridor-map,.product-story,.website-copy,
  .room-metric,.room-door,.room-motion-guide,.room-exit-button{
    background:linear-gradient(135deg,
      rgba(24,25,30,.86),
      rgba(16,17,21,.90) 58%,
      rgba(11,12,16,.92)) !important;
  }
  .product-suite,.website-suite-room{background:rgba(20,21,25,.94) !important}
  .projects-entry-panel{
    background:linear-gradient(135deg,rgba(34,35,41,.95),rgba(15,16,19,.95)) !important;
  }
}

/* ── Wide but very short, above the desktop breakpoint ──────────────────────
   A 1180x260 letterbox takes the desktop layout, which had no short-frame
   guard: the closing room's copy is 371px tall in a 260px section, so it
   overflowed by 108px and its top edge sat above the viewport. The phone side
   has had a short-frame block since the landscape fix; this is the same idea
   for a desktop window dragged into a sliver. Only frames under 560px tall are
   touched, so ordinary desktops never see it. */
@media (min-width:1051px) and (max-height:560px){
  .final-room .final-copy{
    align-self:end;
    width:min(52vw,560px);
    margin-bottom:8px;
    padding:10px 18px;
  }
  .final-room .final-copy small{font-size:8px}
  .final-room .final-copy h2{
    margin:4px 0 6px;
    font-size:min(32px,(min(52vw,560px) - 36px) / 6.2);
  }
  .final-room .final-copy p{font-size:11px;line-height:1.4}
  .final-room .final-copy>div{gap:6px 14px;margin-top:8px}
}

/* Shorter still - the sliver case. Anchoring alone cannot help once the block
   is taller than the gap under the header, so the type takes another step down
   and the eyebrow goes; the heading, the sentence and both actions stay. */
@media (min-width:1051px) and (max-height:340px){
  .final-room .final-copy small{display:none}
  .final-room .final-copy h2{font-size:min(26px,(min(52vw,560px) - 36px) / 6.2)}
  .final-room .final-copy p{font-size:10px;line-height:1.35}
  .final-room .final-copy>div{margin-top:6px}
}

/* ── Double-exposed type ────────────────────────────────────────────────────
   Reported as words looking doubled, including in the opening animation. They
   are: product-premium.css builds a letterpress by stacking HARD copies of the
   glyphs underneath themselves --

     .splash-title      0 1px 0 #fff, 0 3px 0 #8e8e98, 0 6px 0 #484951,
                        0 10px 0 #24252b        <- four solid duplicates
     .splash-title em   three duplicates
     .splash-orbit b    two
     .room-copy h2,
     .room-sign b       three

   Every one of those offsets has ZERO blur, so each is a second rendering of
   the word a few pixels down, not a shadow. At 2.6x device pixel ratio the
   copies resolve separately and the word reads twice. Replaced with a single
   soft shadow: same lift off the render, one set of glyphs, and much crisper
   letterforms -- which is also the "sharper type" half of the request. */
.splash-copy .splash-title,
.splash-copy .splash-title em,
.splash-orbit b,
.room-copy h2,
.room-sign b{
  text-shadow:0 2px 14px rgba(0,0,0,.62),0 1px 3px rgba(0,0,0,.5) !important;
}

/* ── The copy block on phones stops being a bordered box ────────────────────
   Same fault the closing room had, and the same fix. A 1px border round
   .room-copy draws a hard line wherever the block meets the render -- on the
   projects floor that line landed across the two walking figures' chests. The
   box goes; a bottom-anchored fade carries the contrast instead, so there is no
   edge to cut anything with. backdrop-filter goes too: .room-copy was not in
   the list the memory pass cleared, so it was still paying for a blur. */
@media (max-width:1050px){
  .room-copy,.room-copy.copy-right,.room-copy.copy-bottom,.arrival-copy{
    align-self:end;
    width:100%;
    max-width:none;
    margin:0;
    padding:clamp(74px,16svh,150px) 20px max(20px,env(safe-area-inset-bottom));
    border:0 !important;
    border-radius:0 !important;
    box-shadow:none !important;
    backdrop-filter:none !important;
    -webkit-backdrop-filter:none !important;
    background:linear-gradient(180deg,
      transparent 0,
      rgba(8,9,12,.42) clamp(44px,9svh,90px),
      rgba(8,9,12,.88) clamp(74px,16svh,150px),
      rgba(8,9,12,.95) 100%) !important;
  }
  .room-copy>p{max-width:42ch}
}

/* The projects floor's copy is .projects-entry-panel, NOT .room-copy -- the
   .room-copy in that section belongs to the corridor map and measures 0x0 until
   the floor is opened. So the edgeless treatment above missed the one room the
   hard line was actually reported in, and the memory pass had just made it
   worse: dropping backdrop-filter there meant giving it a .95 opaque fill, which
   turned a translucent card into a solid one with a rounded edge sitting across
   the two walking figures' chests. Same fix as everywhere else: floor-anchored,
   full width, fading up out of the render with no edge to cut with. */
@media (max-width:1050px){
  .product-wing-room .projects-entry-panel{
    position:absolute;
    top:auto; right:0; bottom:0; left:0;
    width:100%;
    max-width:none;
    padding:clamp(74px,16svh,150px) 20px max(20px,env(safe-area-inset-bottom));
    transform:none;
    border:0 !important;
    border-radius:0 !important;
    box-shadow:none !important;
    backdrop-filter:none !important;
    -webkit-backdrop-filter:none !important;
    background:linear-gradient(180deg,
      transparent 0,
      rgba(8,9,12,.42) clamp(44px,9svh,90px),
      rgba(8,9,12,.88) clamp(74px,16svh,150px),
      rgba(8,9,12,.95) 100%) !important;
  }
  /* the sheen and the hairline rule would both put an edge back */
  .product-wing-room .projects-entry-panel::before,
  .product-wing-room .projects-entry-panel::after{display:none}
}

/* ══ THE TOUR SCROLLS LIKE A WEBSITE ═══════════════════════════════════════
   Asked for directly: no pinning, no overlap, no snap.

   What produced the overlap: products.css:58 makes every room
   `position:sticky; top:0` with `z-index:calc(30 + var(--room-order))`, so each
   room pins at the top and the NEXT one, being higher in the stack, slides up
   over it. product-quality.css then adds `margin-bottom:100svh` above 1050px,
   which is the stretch of scroll a pinned room sits through. Together that is
   the wipe.

   All three go: the stack returns to normal flow, the gap closes, and snapping
   is off, so a room simply scrolls up and off and the next follows it. This is
   the same configuration the reduced-motion path already used, which is why it
   is known to work rather than newly invented. */
main{scroll-snap-type:none !important}
/* :not(.product-room):not(.website-room) is load-bearing. Those five are
   position:fixed!important overlays opened from the corridor, and this rule has
   the same specificity in a later file -- without the exclusion it wins, and
   they stop being overlays and unroll into the page as 2884px sections. */
.tour-room:not(.product-room):not(.website-room){
  position:relative !important;
  top:auto !important;
  /* height stays DEFINITE. Setting it to auto made the grid row content-sized,
     so align-self:end had no slack left and the closing room's copy drifted up
     and right on a short landscape frame. Only the pinning needed removing. */
  height:100svh;
  min-height:100svh;
  scroll-snap-align:none !important;
  box-shadow:none !important;
}
/* the wipe's own transition veil */
.tour-room::after{display:none !important}
/* close the plateau at every width; the >1050px rule that opened it is above */
.tour-room:not(.product-wing-room):not(.final-room){margin-bottom:0 !important}
.tour-room:not(.product-room):not(.website-room):not(.product-wing-room):not(.final-room){
  min-height:100svh;
}

/* ══ BUTTONS: solid, high contrast, same shape ═════════════════════════════
   Asked for as "1 and 2" -- solid and high contrast, but keep the geometry and
   just sharpen it. So every radius here is inherited, not changed; what changes
   is that the fills stop being translucent glass and the blur goes.

   .office-button was already the model (opaque #e8e9ec, #17181b label, 14.62:1)
   so the primary actions are brought onto it, and the secondary actions get its
   inverse: an opaque dark fill with a firm light edge. */
.office-button,
.projects-entry-button{
  background:#e8e9ec !important;
  color:#17181b !important;
  border:1px solid #e8e9ec !important;
  box-shadow:0 10px 26px rgba(0,0,0,.34) !important;
  backdrop-filter:none !important;
  -webkit-backdrop-filter:none !important;
  text-shadow:none !important;
  font-weight:700;
}
/* the entry button's own children carry their own colours, and on a light fill
   they would be invisible */
.projects-entry-button small{color:#4a4c53 !important}
.projects-entry-button b{color:#17181b !important}
.projects-entry-button>i{
  color:#f4f4f5 !important;
  background:#17181b !important;
  box-shadow:none !important;
}

.exit-link,
.room-exit-button,
.projects-close-button,
.splash [data-splash-skip]{
  background:rgba(13,14,18,.94) !important;
  color:#f4f4f5 !important;
  border:1px solid rgba(255,255,255,.5) !important;
  backdrop-filter:none !important;
  -webkit-backdrop-filter:none !important;
  text-shadow:none !important;
}
.exit-link *,
.splash [data-splash-skip] *{color:inherit !important}

/* The exit button's lit state must still win -- this block is later in the file
   than the mint rule, so without re-asserting it the hover would go back to
   dark-on-dark. That regression has happened here once already. */
.room-exit-button:hover,
.room-exit-button:focus-visible{
  background:var(--mint) !important;
  border-color:var(--mint) !important;
  color:#17181b !important;
  text-shadow:none !important;
}

/* ══ TYPE: heavier and tighter ═════════════════════════════════════════════
   The sans headings take more weight and closer tracking so they read harder.
   The serif italics inside them are left alone -- Cormorant at 800 loses the
   contrast that makes the pairing work. Both faces are variable, so no extra
   font file is fetched for this. */
.room-copy h2,
.final-copy h2,
.projects-entry-panel h2,
.website-copy h2,
.product-title h2{
  font-weight:800;
  letter-spacing:-.088em;
}
.room-copy h2 em,
.final-copy h2 em,
.projects-entry-panel h2 em,
.website-copy h2 em{
  font-weight:500;
  letter-spacing:-.055em;
}

/* ══ THE TWO WINGS ═════════════════════════════════════════════════════════
   The project floor opens onto two boxes -- Apps and Websites -- and Apps
   swaps in a second pane holding the one live app and one placeholder.

   [hidden] is set from JS to switch panes, and an author `display` beats the
   UA's [hidden] rule silently, so both containers state it explicitly. Without
   this the panes would stack on each other and nothing in the markup would look
   wrong. */
.wing-choice[hidden],
.wing-apps[hidden]{display:none !important}

.wing-choice,
.wing-apps{
  display:grid;
  grid-template-columns:repeat(2,minmax(0,1fr));
  gap:12px;
  min-height:0;
  padding:14px 0 0;
  border-top:1px solid rgba(255,255,255,.13);
}
/* align-self/align-content:start, or the corridor grid stretches the single row
   to the full height of the room -- 570px boxes with a lake of empty space
   between the paragraph and the chips. These are cards, not panels. */
.wing-choice,
.wing-apps{align-self:start;align-content:start}
.wing-choice{grid-auto-rows:minmax(200px,auto)}
/* auto rows, with the min on the CARDS -- putting it on the row made the back
   button's row 250px tall and the button stretched into a blob */
.wing-apps{grid-auto-rows:auto}
.wing-apps .room-door,
.wing-apps .wing-idea{min-height:250px}

/* The corridor card is built for the 12-column grid: its placement comes from
   :nth-child and its children are absolutely positioned at percentages tuned to
   that cell (title at left:39%/top:28%, icon at left:24%/top:48%). In a
   half-width cell those overlap -- the Finance title landed on top of its own
   icon and eyebrow. Inside the apps wing the card lays out in flow instead. */
.wing-apps .room-door{
  grid-column:auto !important;
  grid-row:auto !important;
  display:flex;
  flex-direction:column;
  align-items:flex-start;
  gap:10px;
  padding:24px 22px;
}
.wing-apps .room-door>span,
.wing-apps .room-door>img,
.wing-apps .room-door>b,
.wing-apps .room-door>p,
.wing-apps .room-door>i,
.wing-apps .room-door>em{
  position:static !important;
  right:auto !important;left:auto !important;top:auto !important;bottom:auto !important;
  width:auto !important;
  margin:0 !important;
  transform:none !important;
}
.wing-apps .room-door>img{width:46px !important;height:46px !important}
.wing-apps .room-door>b{font-size:clamp(24px,2.6vw,34px) !important;line-height:1;letter-spacing:-.045em}
.wing-apps .room-door>p{max-width:44ch;font-size:12px !important;line-height:1.6}
.wing-apps .room-door>em{
  margin-top:auto !important;
  padding:0 !important;
  border-top:0 !important;
  gap:8px;
}

/* ── a wing box ───────────────────────────────────────────────────────────── */
.wing-box{
  position:relative;
  display:flex;
  flex-direction:column;
  align-items:flex-start;
  gap:10px;
  padding:24px 22px;
  overflow:hidden;
  color:#f4f4f5;
  text-align:left;
  border:1px solid rgba(255,255,255,.42);
  border-radius:16px;
  /* Glass, in the language the rest of the office already speaks: a mostly
     transparent fill so the room shows through, a hairline edge, and an inset
     highlight along the top so the pane catches light. The blur is added
     separately, desktop-only -- see the note at the foot of this file.

     Smoked, not clear. The first version tinted it mid-grey at .15, which is
     see-through but does nothing to a bright patch of the room: measured, a
     warm highlight came through at (116,95,93) and the 9px eyebrow on top of it
     scored 2.27:1. A mid-grey tint cannot fix that -- recomputed at .26 the
     patch was still 0.126 luminance. Only a DARK tint pulls it down, so the
     fill is dark-tinted at ~.3 and the pane stays about 70% transparent. */
  background:linear-gradient(135deg,
    rgba(46,49,58,.28),
    rgba(14,15,20,.32) 58%,
    rgba(9,10,14,.34));
  box-shadow:
    inset 0 1px rgba(255,255,255,.34),
    inset 0 -1px rgba(255,255,255,.09),
    0 22px 48px rgba(0,0,0,.34);
  text-shadow:0 1px 2px rgba(0,0,0,.8),0 2px 10px rgba(0,0,0,.5);
  cursor:pointer;
  transition:transform .35s var(--ease),border-color .35s,box-shadow .35s;
}
/* The eyebrow was #9da0a6, which was fine on the old near-opaque fill and is
   not on glass: at 9px over the brightest patch the room pushes through it
   measured 2.27:1. Computed against that patch under the tinted fill, 4.5:1
   needs about 197; this is 200, so it clears with a little margin. */
.wing-box>span{color:#c8cacf;font-size:9px;font-weight:600;letter-spacing:.14em;text-transform:uppercase}
.wing-box>b{font-size:clamp(28px,3.4vw,46px);font-weight:800;line-height:.98;letter-spacing:-.05em}
.wing-box>p{max-width:42ch;margin:0;color:#c3c5ca;font-size:13px;line-height:1.6}
.wing-box>i{
  margin-top:auto;padding:7px 11px;color:#d9dadd;border:1px solid rgba(255,255,255,.2);
  border-radius:999px;background:rgba(255,255,255,.06);
  font-size:8px;font-style:normal;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
}
.wing-box>em{
  display:flex;align-items:center;gap:8px;
  color:#f4f4f5;font-size:9px;font-style:normal;font-weight:600;
  letter-spacing:.1em;text-transform:uppercase;
}
.wing-box>em u{text-decoration:none;font-size:13px;transition:transform .3s var(--ease)}
.wing-box:hover,.wing-box:focus-visible{
  border-color:rgba(255,255,255,.7);
  box-shadow:inset 0 1px rgba(255,255,255,.14),0 30px 62px rgba(0,0,0,.4);
  transform:translateY(-4px);
}
.wing-box:hover>em u,.wing-box:focus-visible>em u{transform:translate(2px,-2px)}
.wing-box-apps{--wing-accent:114,224,196}
.wing-box-web{--wing-accent:183,166,255}
.wing-box::after{
  content:"";position:absolute;right:0;bottom:0;left:0;height:2px;
  background:linear-gradient(90deg,transparent,rgba(var(--wing-accent),.9),transparent);
  opacity:.6;
}

/* ── the placeholder: an app that has not been decided ────────────────────── */
.wing-idea{
  position:relative;
  display:flex;
  flex-direction:column;
  align-items:flex-start;
  gap:10px;
  padding:24px 22px;
  color:#c3c5ca;
  border:1px dashed rgba(255,255,255,.3);
  border-radius:16px;
  /* the same glass, dialled down -- it is a placeholder, so it should read as
     the quieter pane of the three */
  background:repeating-linear-gradient(135deg,rgba(255,255,255,.03) 0 10px,transparent 10px 20px),
    linear-gradient(135deg,rgba(46,49,58,.24),rgba(9,10,14,.3));
  box-shadow:inset 0 1px rgba(255,255,255,.2);
  text-shadow:0 1px 2px rgba(0,0,0,.8),0 2px 10px rgba(0,0,0,.5);
}
.wing-idea>span{color:#c8cacf;font-size:9px;font-weight:600;letter-spacing:.14em;text-transform:uppercase}
.wing-idea-glyph{
  display:grid;place-items:center;width:46px;height:46px;
  color:#9da0a6;border:1px dashed rgba(255,255,255,.28);border-radius:14px;
  font-size:20px;font-style:normal;font-weight:700;
}
.wing-idea>b{color:#e8e9ec;font-size:clamp(20px,2.2vw,28px);font-weight:800;line-height:1;letter-spacing:-.04em}
.wing-idea>p{max-width:44ch;margin:0;font-size:12px;line-height:1.65}
.wing-idea>i{
  margin-top:auto;padding:6px 10px;color:#9da0a6;
  border:1px solid rgba(255,255,255,.14);border-radius:999px;background:rgba(255,255,255,.04);
  font-size:8px;font-style:normal;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
}

.wing-back{
  grid-column:1/-1;
  justify-self:start;
  align-self:start;
  position:relative !important;
  right:auto !important;
  top:auto !important;
  width:auto;
  min-height:34px;
  margin-top:2px;
}

@media (max-width:720px){
  .wing-choice,.wing-apps{grid-template-columns:1fr;gap:10px}
  .wing-choice{grid-auto-rows:auto}
  .wing-box{padding:18px 16px;gap:8px}
  .wing-box>p{font-size:12px}
  .wing-idea{padding:18px 16px}
}

/* ── The back controls were never given a thumb-sized target ────────────────
   The coarse-pointer block near the top of this file lists .tour-brand,
   .exit-link, the splash skip, .office-button, .product-actions a and
   .final-copy div a -- but not these two, which are the ones a thumb actually
   reaches for. Measured on the live site at 411x757: "Back to entrance" 121x30,
   "Back to the wings" 123x34, "Back to project corridor" 171x40. All three are
   under WCAG 2.5.8's 44px, and .projects-close-button asks for 30px outright.

   Only under (pointer: coarse) -- a mouse is precise and the desktop rhythm of
   these small pills is deliberate. */
@media (pointer: coarse){
  .projects-close-button,
  .room-exit-button{
    min-height:44px !important;
    display:inline-flex;
    align-items:center;
  }
}

/* ── The second action in a product room was a 9px text link ────────────────
   products.css:13 styles `.product-actions>a:not(.office-button)` as 9px type
   with a border-bottom, sitting immediately beside a solid 10px button. In the
   Finance room that is the Google Play link, and next to "Open web app" it read
   as an afterthought rather than the other half of a pair. Same underline
   problem the closing room had, too: the rule above gives it min-height:42px as
   an inline-flex box, so the border-bottom parks well under the words.

   It becomes the secondary button already used everywhere else -- opaque dark
   fill, firm light edge -- and takes the 2px radius of the button it stands
   next to, so the pair reads as a pair. */
.product-actions>a:not(.office-button):not(.dl-badge){
  min-height:44px;
  padding:0 16px;
  color:#f4f4f5 !important;
  font-size:10px;
  font-weight:600;
  letter-spacing:.06em;
  text-transform:uppercase;
  text-decoration:none;
  border:1px solid rgba(255,255,255,.5) !important;
  border-bottom-color:rgba(255,255,255,.5) !important;
  border-radius:2px;
  background:rgba(13,14,18,.94) !important;
  transition:border-color .25s,background .25s,color .25s;
}
.product-actions>a:not(.office-button):not(.dl-badge):hover,
.product-actions>a:not(.office-button):not(.dl-badge):focus-visible{
  color:#17181b !important;
  border-color:var(--mint) !important;
  background:var(--mint) !important;
}

/* ── The Finance room shows a finished composite, not a framed screenshot ────
   .display-window is a device frame: a rotated, bordered, #18191c box holding a
   PORTRAIT screenshot with object-fit:cover. The dashboard shot was being
   cover-cropped into it, which cut the right-hand third of the interface off,
   and a second screenshot floated over the corner on top of that.

   The replacement is a single landscape composite -- five real screens fanned,
   already carrying its own ground and labels -- so the frame around it is
   wrong by definition. The window keeps its scroll-driven motion and loses
   everything that framed it. */
.screens-window{
  left:4% !important;
  right:4% !important;
  top:9% !important;
  height:66% !important;
  padding:0 !important;
  border:0 !important;
  border-radius:0 !important;
  background:transparent !important;
  box-shadow:none !important;
}
.screens-window>img:first-child{
  width:100% !important;
  height:100% !important;
  object-fit:contain !important;
  object-position:center !important;
  border-radius:0 !important;
}

/* ── Both product actions have to be reachable on a phone ───────────────────
   .product-actions is stacked into a column below 720px, which was fine when
   the second action was a 9px text link. Now that it is a 44px button the pair
   is 98px tall, and measured on the device at 411x757: "Open web app" landed at
   702-746 -- colliding with the motion guide bar at 716-747 -- and "Google Play"
   at 756-800, entirely below the fold and hit-testing as unreachable.

   They go back in a row: 119px + 116px + gap fits inside 411 with room spare.
   The story also gets bottom padding so the last row clears the guide bar
   instead of sitting under it. */
@media (max-width:720px){
  .product-actions{
    flex-direction:row !important;
    flex-wrap:wrap;
    align-items:center !important;
    gap:10px;
  }
  /* padding on the story does nothing here: .product-actions carries
     margin-top:auto, so it is pinned to the container's bottom edge whatever
     the padding is. Measured after the padding attempt: still 716-760, sitting
     on the guide bar at 716-747. The margin has to go on the actions row. */
  /* The guide bar is position:fixed over the bottom ~45px of the viewport, so
     the row has to END above it, not merely be inside the viewport. Measured:
     at 58px the row sat 698-742 against a bar starting at 716. 92px clears it. */
  .product-actions{margin-bottom:92px}
}

/* ── The official Google Play badge ─────────────────────────────────────────
   Google's brand guidelines want their own badge artwork, whole and
   undistorted -- not a button that says "Google Play". This is the same
   assets/google-play-badge.png that finance-tracker.html, index.html and
   products.html already link with, so the office matches the rest of the site.

   It is excluded from the secondary-button rule above, or that rule would
   paint a dark pill and a border behind artwork that already has its own. The
   44px height matches "Open web app" beside it, so the pair sits level and the
   badge still clears the coarse-pointer target size on its own. */
.product-actions .dl-badge{
  display:inline-flex;
  align-items:center;
  min-height:44px;
  padding:0 !important;
  border:0 !important;
  border-radius:9px;
  background:none !important;
  transition:transform .25s var(--ease),filter .25s;
}
.product-actions .dl-badge img{display:block;height:44px;width:auto}
.product-actions .dl-badge:hover,
.product-actions .dl-badge:focus-visible{transform:translateY(-1px);filter:brightness(1.08)}
.product-actions .dl-badge:focus-visible{outline:2px solid var(--mint);outline-offset:4px}

/* ── The board the wings sit on is glass too ────────────────────────────────
   Measured glyph-tight on the composited frame: the board's own copy was on
   the bare render, and a ceiling light crosses exactly where it sits -- the
   heading came back 2.36:1 and the paragraph 2.73:1 against a (179,157,134)
   patch. Making the board the same smoked glass as the panes fixes both while
   staying about half see-through, and it means the whole screen is one
   material rather than glass boxes floating on nothing. */
.product-wing-room.projects-open .corridor-map{
  background:linear-gradient(145deg,
    rgba(20,21,26,.46),
    rgba(13,14,18,.5) 58%,
    rgba(10,11,15,.52)) !important;
  border-color:rgba(255,255,255,.2);
}
/* #aeb0b6 was set for an opaque board. On glass it does not hold: at .66 over
   the brightest patch it computes 3.68:1, and 208 brings that to 5.29:1. */
.corridor-heading p{color:#d0d2d6;text-shadow:0 1px 2px rgba(0,0,0,.8),0 2px 10px rgba(0,0,0,.5)}
.corridor-heading h2,
.corridor-heading>span{text-shadow:0 2px 14px rgba(0,0,0,.62),0 1px 3px rgba(0,0,0,.5)}

/* ── The app card is glass as well ──────────────────────────────────────────
   It stands directly beside the placeholder, so leaving it the only solid pane
   on the screen read as something missed rather than as hierarchy -- and the
   hierarchy is already carried by its teal edge and glow, which stay. Scoped to
   the wing: .room-door is also the corridor map's card and that one is not part
   of this. The teal is kept as a faint cast in the fill's first stop. */
.wing-apps .room-door{
  border-color:rgba(255,255,255,.4) !important;
  background:linear-gradient(135deg,
    rgba(42,58,55,.3),
    rgba(13,15,19,.34) 58%,
    rgba(9,11,14,.36)) !important;
  box-shadow:
    inset 0 1px rgba(255,255,255,.32),
    inset 0 -1px rgba(255,255,255,.08),
    0 22px 48px rgba(0,0,0,.34) !important;
  text-shadow:0 1px 2px rgba(0,0,0,.8),0 2px 10px rgba(0,0,0,.5);
}
.wing-apps .room-door-featured{
  border-color:rgba(var(--wing-accent,114,224,196),.5) !important;
}
@media (min-width:1051px){
  .wing-apps .room-door{backdrop-filter:blur(4px) saturate(1.14);-webkit-backdrop-filter:blur(4px) saturate(1.14)}
}
@media (max-width:1050px){
  .wing-apps .room-door{
    backdrop-filter:none !important;-webkit-backdrop-filter:none !important;
    background:linear-gradient(135deg,
      rgba(42,58,55,.38),
      rgba(13,15,19,.42) 58%,
      rgba(9,11,14,.44)) !important;
  }
}

/* ── Where the wings get their blur ─────────────────────────────────────────
   The glass fills are set on .wing-box/.wing-idea above. The blur lives here
   on its own, gated to the desktop, because backdrop-filter was deliberately
   stripped from every panel below 1051px as part of the fix that stopped the
   tour crashing on a phone -- each blurred pane forces its own backdrop
   snapshot, and the wings are two or three more of them on one screen.

   So the phone gets glass without blur: the same translucent fill carrying a
   little more weight, plus the edge and the inset highlight, which is what
   actually reads as glass at that size. */
@media (min-width:1051px){
  .wing-box,
  .wing-idea{
    backdrop-filter:blur(4px) saturate(1.14);
    -webkit-backdrop-filter:blur(4px) saturate(1.14);
  }
}
@media (max-width:1050px){
  .wing-box,
  .wing-idea{backdrop-filter:none !important;-webkit-backdrop-filter:none !important}
  /* Glass needs something behind it. The memory pass gave .corridor-map a .86
     fill to replace the blur it lost, and the wings sit inside .corridor-map --
     so on a phone the panes were transparent onto an opaque board and read as
     plain dark boxes, which is the opposite of the point. Measured on the
     composited frame, not guessed: the heading is large and white with its own
     shadow and holds easily at this weight, and every pane's copy is re-scored
     below. Only the wings state is opened up; the corridor keeps its fill. */
  .product-wing-room.projects-open .corridor-map{
    background:linear-gradient(145deg,
      rgba(24,25,30,.66),
      rgba(14,15,19,.7) 58%,
      rgba(11,12,16,.72)) !important;
  }
  .wing-box{
    background:linear-gradient(135deg,
      rgba(46,49,58,.36),
      rgba(14,15,20,.4) 58%,
      rgba(9,10,14,.42)) !important;
  }
  .wing-idea{
    background:repeating-linear-gradient(135deg,rgba(255,255,255,.03) 0 10px,transparent 10px 20px),
      linear-gradient(135deg,rgba(46,49,58,.3),rgba(9,10,14,.38)) !important;
  }
}

/* ══ ONE CORNER RADIUS FOR EVERY CONTROL ═══════════════════════════════════
   Asked for directly. The audit found EIGHT distinct radii across the tour's
   controls -- 0, 2, 9, 14, 15, 16, 18 and 999px -- which is not a system, it is
   eight separate decisions. 2px is the one to keep: it is already what
   .office-button and .room-exit-button use, and products.css:113 squares the
   suite, the corridor map and the room doors on a phone, so the rest were the
   outliers rather than the rule.

   The inner chip of .projects-entry-button is included -- it sits inside the
   button, so a 14px pill inside a 2px box would read as a mistake. The Play
   badge is included for consistency but nothing changes visually: the link has
   no fill, and the artwork carries its own corners.

   Deliberately NOT included: the small status labels (LIVE NOW, THINKING,
   PROJECTS OPEN, the feature chips). Those are not controls, and squaring them
   was not what was asked. */
.tour-brand,
.skip-link,
.office-button,
.projects-entry-button,
.projects-entry-button>i,
.exit-link,
.premium-exit,
.room-exit-button,
.projects-close-button,
.wing-back,
.wing-box,
.wing-idea,
.wing-idea-glyph,
.room-door,
.product-actions>a,
.product-actions .dl-badge,
.final-copy div a,
.splash [data-splash-skip]{
  border-radius:2px !important;
}

/* ══ THE FINANCE ROOM ON A PHONE ═══════════════════════════════════════════
   Two faults, reported together and measured before either was touched.

   1. The screens were not visible. They were not missing -- measured on the
      device, the composite rendered at 351x356 with top:824 in a 757px
      viewport, i.e. below the fold, and below the copy as well. That is a
      consequence of the reveal above: .product-room .product-suite starts at
      top:40svh so the render holds the top of the screen, which pushes the
      card's SECOND row two folds down. Putting the display first on a phone is
      what fixes it -- the card now opens on the five screens, with the name and
      the buttons under them, which is also the order a store listing uses.

      The window is absolutely positioned at a percentage of .product-display,
      so the display needed a real height to size it. An aspect-ratio rather
      than a pixel height, so it holds at every width: 5/4 gives 382x306 at
      411px, the window fills 232 of that, and the 2400x1560 composite lands
      351x228 inside it with no letterboxing worth the name.

   2. The card was not transparent. The memory pass had to give it a .94 fill
      when it lost backdrop-filter, and .product-story sits on top of that at
      .86, so the two together were effectively opaque -- the glass everywhere
      else stopped at this card. Both drop to a fill that composites to about
      .45 over the render. Still no blur below 1051px: that part of the crash
      fix stays. Contrast re-measured on the composited frame afterwards. */
@media (max-width:1050px){
  .product-suite,
  .website-suite-room{
    background:linear-gradient(135deg,
      rgba(46,49,58,.22),
      rgba(9,10,14,.26)) !important;
  }
  /* Deeper than the wings, and for a reason: this pane carries a paragraph, a
     feature list and two labels at 10px, and the Finance room's render has a
     pale floor behind exactly where they sit -- measured, the render reads ~189
     there, and the first attempt at .45 coverage put the eyebrow at 1.74:1.
     The panes are not all the same alpha; they are all the same material at
     whatever alpha their own backdrop needs. This composites to about .69, so
     roughly a third of the room still shows through. */
  .product-story,
  .website-copy{
    background:linear-gradient(135deg,
      rgba(40,43,52,.6),
      rgba(12,13,18,.64) 58%,
      rgba(9,10,14,.66)) !important;
    text-shadow:0 1px 2px rgba(0,0,0,.8),0 2px 10px rgba(0,0,0,.5);
  }
  /* same lift the wings needed, same reason */
  .product-title small{color:#c8cacf}
  /* The guide bar is position:fixed over the bottom of the viewport, so
     whatever the card has at the bottom edge sits under it -- unavoidable with
     a fixed bar and a scrolling card. At .86 that was not occlusion but
     GHOSTING: "ANDROID & WEB" and the top of "Finance Tracker" were legible
     through the bar, tangled with its own label. This is what the contrast
     sweep was really reporting at 1.74:1 -- raising the pane's fill from .45 to
     .69 moved that patch by 0px, which is the tell that it was never behind the
     glass. An overlay that cannot blur has to be opaque. */
  .room-motion-guide{
    background:linear-gradient(135deg,#191a1f,#111216 58%,#0d0e12) !important;
  }
  .product-display{
    background:
      radial-gradient(circle at 55% 26%,rgba(175,176,178,.14),transparent 58%),
      linear-gradient(135deg,rgba(46,49,58,.3),rgba(9,10,14,.36)) !important;
  }
}
/* Applied at the single-column breakpoint rather than 720px: the column
   collapse is what buries the second row, and that happens at 1050. */
@media (max-width:1050px){
  .product-room .product-display{
    order:-1;
    min-height:0 !important;
    height:auto !important;
    aspect-ratio:5/4;
    /* so a wide tablet does not hand the composite the whole screen */
    max-height:44svh;
  }
  .product-room .screens-window{top:5% !important;height:78% !important}
  /* The dead band under the screens was not a gap. product-motion.css:71 pins
     the card at `min-height:1040px !important` (1120 above 720px), a figure
     sized for the old 650px-tall display. With the display now ~305 the grid
     had ~100px of slack and, align-content being `normal`, it stretched the
     rows rather than shrinking the card -- so the display's ROW measured 406px
     around a 305px box. Releasing the minimum lets the card be its content.
     Only the product rooms: the website room's layout is unchanged and still
     wants its floor. */
  .product-room .product-suite{min-height:0 !important;gap:10px !important}
}

/* ══ THE CLIENT MARK IN THE WEBSITE ROOM ═══════════════════════════════════
   Asked for: a small transparent box under the info, carrying the SLN Xpress
   logo. The artwork is the one the live site actually serves --
   slnxpress.in/static/img/logo.png, 661x160 with real alpha -- rather than the
   square app mark in the brand folder, which is a different lockup from the one
   on their header. The transparency matters: on glass a logo with a baked
   background would read as a white sticker.

   Same material as every other pane, same 2px corner, blur desktop-only. It is
   deliberately not a link -- "Visit SLN Xpress" is directly above it, and a
   second control to the same place is a second thing to decide about. */
.client-mark{
  display:inline-flex;
  flex-direction:column;
  align-items:flex-start;
  gap:9px;
  width:max-content;
  max-width:100%;
  margin-top:20px;
  padding:13px 16px;
  border:1px solid rgba(255,255,255,.34);
  border-radius:2px;
  background:linear-gradient(135deg,rgba(46,49,58,.2),rgba(9,10,14,.24));
  box-shadow:inset 0 1px rgba(255,255,255,.28);
}
.client-mark img{display:block;width:auto;height:32px}
.client-mark span{
  color:#c8cacf;
  font-size:8px;
  font-weight:600;
  letter-spacing:.14em;
  text-transform:uppercase;
  text-shadow:0 1px 2px rgba(0,0,0,.8);
}
@media (min-width:1051px){
  .client-mark{backdrop-filter:blur(4px) saturate(1.14);-webkit-backdrop-filter:blur(4px) saturate(1.14)}
}
@media (max-width:1050px){
  .client-mark{
    backdrop-filter:none !important;
    -webkit-backdrop-filter:none !important;
    background:linear-gradient(135deg,rgba(46,49,58,.34),rgba(9,10,14,.4)) !important;
  }
}

/* The 92px was added so the actions row would clear the fixed guide bar when it
   was the LAST thing in the card. In the website room it no longer is -- the
   client mark is -- so the clearance has to move with whatever ends the card,
   or the room gets a 92px hole between the buttons and the mark. */
@media (max-width:720px){
  .product-actions{margin-bottom:0}
  .product-story>.product-actions:last-child,
  .website-copy>.product-actions:last-child,
  .website-copy>.client-mark:last-child{margin-bottom:92px}
}

/* ── The mid-height desktop band, where the client mark did not fit ─────────
   The card is a fixed calc(100svh - 104px), so a block added at the bottom of
   the copy column either fits or is silently cut. Measured across five desktop
   sizes: 1920x1080, 1366x768, 1280x720 and 1100x1000 all fit -- the short ones
   because the max-height:760px rule above already compacts them -- and only
   1440x900 overflowed, by 89px, because it is tall enough to miss that rule and
   wide enough for a 260px heading.

   So this is the missing middle, and it is deliberately mild: the fixed margins
   come down and the heading is left alone. .product-actions carries
   margin-top:auto, so wherever there IS slack these reductions just grow the
   auto gap and nothing moves -- they only take effect where the column is
   actually short, which is exactly the band that was clipping. Capped at
   1040px so the 1080 layout is untouched. */
@media (min-width:1051px) and (min-height:761px) and (max-height:1040px){
  .website-copy{padding-bottom:28px !important}
  .website-copy>small{margin-top:24px !important}
  .website-copy h2{margin-bottom:16px !important}
  .website-copy>p{margin-top:16px !important}
  .website-copy .room-tags{margin-top:16px !important}
  .client-mark{margin-top:12px;padding:9px 13px;gap:7px}
  .client-mark img{height:24px}
}

/* ── The card must not be pinned taller than the screen ─────────────────────
   Putting the display first was right, but it moved the action buttons into
   the card's second row -- and the card is position:sticky at top:40svh, so it
   NEVER moves. Measured on the device: sticky top 302.78px, card 837px,
   viewport 757. The bottom 383px, both buttons included, could not be reached
   at any scroll position; setting the room's scrollTop to 600 read back as 600
   and moved the card 0px, and scrollIntoView got them only to 871.

   That is not "below the fold", it is unreachable, and the sticky pin is the
   whole reason. A pinned box taller than the viewport can never show its own
   bottom. The reveal is kept -- margin-top:40svh still holds the render at the
   top of the screen -- but the card scrolls away with the room like anything
   else, which is also what was asked for the tour generally.

   Only the product rooms, and only where the column is single: on the desktop
   the card is two columns and shorter than the viewport, so it is fine pinned. */
@media (max-width:1050px){
  .product-room .product-suite{
    position:relative !important;
    top:auto !important;
    bottom:auto !important;
  }
}

/* ── The scroll track was sized for a PINNED card ───────────────────────────
   .room-motion-track is a 150svh spacer that gives the room enough scroll to
   drive its choreography. That was invisible while the card was pinned -- the
   track scrolled behind it. With the card scrolling normally you scroll past
   it and then meet 1135px of empty black before the next room.

   The card itself is what needs the scroll now: 303px of reveal plus an 837px
   card is 1140, and a 757px viewport needs 383px of travel to reach its foot.
   12svh leaves a little slack past that without the void. */
@media (max-width:1050px){
  .product-room .room-motion-track{height:12svh}
}

/* ── The website room has the same pinned-card problem ──────────────────────
   Measured on the device: .website-suite-room is position:sticky at top:66px,
   1245px tall, in a 757px viewport. Its controls are all fine -- both buttons
   and the client mark hit-test TAPPABLE on open, at 511-654 -- but the card's
   bottom 488px holds the .product-display with the SLN Xpress site screenshot,
   and a pinned box taller than the screen can never show its own foot. So that
   screenshot was unreachable on a phone, exactly like the Finance screens were.

   Unpinned, not reordered. The Finance room puts its display first because the
   fanned screens are that room's subject; here the headline is, and swapping
   the order would be an editorial change nobody asked for. Unpinning alone
   makes the screenshot reachable by scrolling, which is the actual defect. */
@media (max-width:1050px){
  .website-room .website-suite-room{
    position:relative !important;
    top:auto !important;
    bottom:auto !important;
  }
  .website-room .room-motion-track{height:12svh}
}

/* ── The website room loses its screenshot ──────────────────────────────────
   Removed on request. .website-suite-room is a two-column grid (.72fr 1.28fr)
   and the screenshot filled the wide side, so deleting it alone would have left
   a half-empty glass card with the copy squeezed into the narrow third. It
   becomes a single editorial column instead, sized so the studio render carries
   the rest of the frame rather than an empty panel doing it.

   The mobile minimum goes too: product-motion.css pins the card at
   min-height:1040px !important, which was sized for copy PLUS a 650px display.
   With only the copy that is a void under the client mark. */
@media (min-width:1051px){
  .website-room .website-suite-room{
    grid-template-columns:1fr !important;
    /* 860px made the copy too short for a card whose height is fixed at
       calc(100svh - 104px): .product-actions carries margin-top:auto, so the
       slack collected as a ~260px void between the chips and the buttons. The
       old two-column layout gave the copy about 450px, which is why it never
       showed. Back to a similar measure and the column fills again. */
    width:min(46vw,640px) !important;
  }
}
@media (max-width:1050px){
  .website-room .website-suite-room{min-height:0 !important}
}

/* ══ THE WEBSITE ROOM ON A PHONE: NO CARD, TYPE ON THE RENDER ══════════════
   Asked for directly -- the glass card goes and the copy floats on the room.

   Legibility has to come from somewhere, and it must NOT come from a panel
   behind the type, or the card is simply back with softer edges. It comes from
   the render instead: .room-vignette is already full-bleed and edgeless, so
   deepening it darkens the whole scene behind the copy with no boundary
   anywhere. That is the same move the closing room uses. The type carries a
   tight shadow on top of it.

   The vignette is weighted to the left and the foot, which is where the copy
   sits, and stays light across the upper right so the framed print and the
   monitor -- the two things the render is now FOR -- are not buried.

   The scroll guide goes too. It was a hint for a choreography that barely
   applies now the card scrolls normally, and it is a fixed bar that has to be
   drawn over whatever the card has at the bottom edge. */
@media (max-width:1050px){
  .website-room .website-suite-room,
  .website-room .website-copy{
    background:none !important;
    border:0 !important;
    box-shadow:none !important;
    backdrop-filter:none !important;
    -webkit-backdrop-filter:none !important;
  }
  .website-room .website-copy{
    text-shadow:0 2px 12px rgba(0,0,0,.92),0 1px 3px rgba(0,0,0,.88);
  }
  .website-room > .room-vignette.strong{
    background:
      linear-gradient(180deg,
        rgba(8,9,12,.29) 0%,
        rgba(8,9,12,.56) 38%,
        rgba(8,9,12,.78) 78%,
        rgba(8,9,12,.87) 100%),
      linear-gradient(90deg,
        rgba(8,9,12,.46) 0%,
        rgba(8,9,12,.17) 62%,
        rgba(8,9,12,.03) 100%) !important;
  }
  /* product-motion.css:4 is `.room-motion-guide.is-control-active{display:grid
     !important}` -- two classes to my one, and !important on both sides, so
     specificity decides and mine lost. It only ever hid the INACTIVE guide,
     which is zero-sized anyway; the one on screen was untouched. The computed
     value said `none` too, because querySelector returns the first match and
     that was the wrong element. Match the specificity. */
  .room-motion-guide,
  .room-motion-guide.is-control-active{display:none !important}

  /* .room-exit-button is fixed at top:73px and 44px tall, so it lands on the
     status row: "CLIENT WING" was sitting under the "Back to project corridor"
     button and scored 1.10:1 against its near-white fill. That is occlusion,
     not contrast -- it predates the card being removed, it was just invisible
     while there was a card. Start the copy below the button. */
  /* Asked for: drop the copy so the render shows above it. 74px cleared the
     exit button and nothing else -- the copy started at y140 while the framed
     print and the monitor run to y305-480 depending on ratio.

     Mapped rather than guessed. .room-bg is object-fit:cover at 118vw, so the
     image-space boxes land somewhere different at every ratio, and the portrait
     and landscape sources have different coordinates besides. Computing the
     cover transform from the element's own rect and natural size and mapping
     both boxes through it: the artwork's foot sits at 47-53% of viewport height
     across 360x640, 411x757, 390x844, 768x1024 and 1015x767.

     46svh of padding puts the copy below all of them with 50px to spare at the
     tightest, and the clamp keeps it sane on a letterbox phone. The copy now
     runs past the fold, which is fine -- the card was unpinned earlier, so it
     scrolls, and reachability was hit-tested on the device. */
  .website-room .website-copy{padding-top:clamp(110px,46svh,420px) !important}

  /* With no card behind it, the LIVE NOW chip sits straight on the wall -- and
     at that height the wall is the lit part, measured (109,97,82). Mint on that
     is 3.78:1. A chip on imagery needs to carry its own ground, so it gets one;
     this is a 90x22 pill, not a panel behind the copy. */
  .website-room .product-status b{
    background:rgba(11,13,16,.85) !important;
    backdrop-filter:none !important;
    -webkit-backdrop-filter:none !important;
  }
}

/* ══ THE WEBSITE ROOM'S ACTION BLOCK, REBUILT ══════════════════════════════
   What was wrong: the four capability chips were inline-wrapped, so they broke
   3 + 1 with a stranded "FRONTEND BUILD" and four different widths; the two
   buttons sat in one row at unrelated widths in two different styles; and the
   logo box sat below with dead space beside it.

   Now it is three aligned rows sharing one left edge:
     chips   -- a grid, so they are equal width and the block has square edges
     primary -- "Visit SLN Xpress" alone, the one thing this room is for
     footer  -- the logo box with "Discuss a website" at the far right

   The column COUNT is set explicitly rather than left to auto-fit. auto-fit
   picks the count from the available width, and the two ends want opposite
   things: a 371px phone column needs a track >=124px to stop at two columns,
   while the desktop copy column is 524px of content -- not the 640 the card is
   wide, the padding is 58px a side -- and needs <=125px to reach four. That is
   a 1px window, so a track width that is right at one end is wrong at the
   other. Fixed counts cannot land 3 + 1 at any width. */
.website-room .room-tags{
  display:grid;
  grid-template-columns:repeat(2,minmax(0,1fr));
  gap:8px;
  margin-top:18px;
}
@media (min-width:1200px){
  .website-room .room-tags{grid-template-columns:repeat(4,minmax(0,1fr))}
}
.website-room .room-tags span{
  display:flex;
  align-items:center;
  justify-content:center;
  min-height:36px;
  margin:0;
  padding:0 10px;
  border-radius:2px;
  text-align:center;
}
.website-room .product-actions{
  display:block;
  margin-top:10px;
}
.website-room .product-actions>.office-button{
  display:flex;
  align-items:center;
  justify-content:center;
  width:100%;
  min-height:48px;
}
@media (min-width:1051px){
  /* a 640px-wide button is not a button, it is a bar */
  .website-room .product-actions>.office-button{width:max-content;min-width:250px}
}

/* the footer row: the mark holds its size, the CTA is pushed to the far right
   and matches its height, so the two read as one band rather than two strays */
.client-row{
  display:flex;
  align-items:stretch;
  gap:10px;
  margin-top:10px;
}
.client-row .client-mark{margin-top:0}
.client-cta{
  display:flex;
  align-items:center;
  justify-content:center;
  margin-left:auto;
  min-height:44px;
  max-width:46%;
  padding:0 18px;
  color:#f4f4f5;
  border:1px solid rgba(255,255,255,.5);
  border-radius:2px;
  background:rgba(13,14,18,.94);
  font-size:10px;
  font-weight:600;
  letter-spacing:.06em;
  line-height:1.3;
  text-align:center;
  text-decoration:none;
  text-transform:uppercase;
  transition:color .25s,border-color .25s,background .25s;
}
.client-cta:hover,
.client-cta:focus-visible{
  color:#17181b;
  border-color:var(--mint);
  background:var(--mint);
}

/* ── The desktop card has to answer the new render ──────────────────────────
   Adding the framed print and the lit monitor put two bright objects directly
   behind where the desktop card sits, and that card is glass at ~.66. Measured
   after the render change: the LIVE NOW chip fell to 2.67:1 at 1920x1080, the
   eyebrow to 3.23:1 at 1200x900 and the heading to 4.02:1 -- against backdrops
   reading up to (138,127,115), which is the print coming through the glass.
   None of that existed before, because the old render had blank monitors and a
   bare wall there.

   The phone does not need this: its card is gone entirely and the vignette
   handles it. This is the desktop card carrying a little more of its own
   weight -- still translucent, the print and the monitor still read through
   it, just no longer bright enough to swallow 10px type. */
   FIRST ATTEMPT WAS WRONG AND IS RECORDED HERE. Deepening the card to .84/.90
   did pass -- 7.35:1 at 1920 -- and buried the print and the monitor almost
   completely. Contrast was bought by deleting the thing that had just been
   added. The failures were not general: they were four small items, each of
   which can carry its own ground or be brightened, so they are fixed at source
   and the card stays glass. */
@media (min-width:1051px){
  .website-room .website-suite-room{
    background:linear-gradient(135deg,
      rgba(20,21,26,.70),
      rgba(13,14,18,.74) 58%,
      rgba(10,11,15,.76)) !important;
  }
}
/* chips and the status pill sit on the render at every width now, so they
   carry their own ground rather than borrowing the card's */
.website-room .room-tags span{
  color:#e2e3e6;
  border-color:rgba(255,255,255,.26);
  background:rgba(11,13,16,.72);
  text-shadow:0 1px 2px rgba(0,0,0,.8);
}
.website-room .product-status b{
  background:rgba(11,13,16,.85);
}
/* The two eyebrows cannot be fixed by colour. Behind them the framed print
   comes through at (139,127,109) -- luminance .218 -- and 4.5:1 against that
   needs a text luminance of 1.155, which does not exist. Brightening them to
   #dcdee2 got 2.91:1 and could not have got further. They need a darker
   backdrop, so they get their own, the same way the chips and the LIVE NOW
   pill already do. Both sit beside or above elements that are already plated,
   so this reads as the block's own language rather than a patch. */
.website-room .product-status span,
.website-room .website-copy>small{
  display:inline-flex;
  align-items:center;
  align-self:flex-start;
  width:max-content;
  max-width:100%;
  padding:5px 9px;
  color:#e4e5e8;
  border:1px solid rgba(255,255,255,.16);
  border-radius:2px;
  background:rgba(10,12,15,.82);
  text-shadow:0 1px 2px rgba(0,0,0,.85);
}

/* ══ THE TWO NAVIGATION CONTROLS, REDESIGNED ═══════════════════════════════
   The back control and the exit control do the same kind of job -- leave where
   you are -- and looked unrelated: one was a thin "←" character with a label,
   the other a label with a light plate holding a generic "↗". Neither arrow was
   really an icon; at 10px a glyph is thin, it renders differently per font
   fallback, and it cannot be given a shape of its own.

   They are one family now, mirrored: the back control puts a drawn arrow in a
   well on the LEFT, the exit control keeps its light plate on the RIGHT and
   holds a proper door-and-arrow rather than a diagonal. Same 2px corner, same
   24px well, same 1.6px-equivalent stroke, and the arrow moves on hover in the
   direction the control actually goes.

   Applied to all three back controls -- the room exit built in products.js,
   "Back to entrance" and "Back to the wings" -- not just the one in view. */
.ctl-icon{
  display:inline-grid;
  place-items:center;
  flex:0 0 24px;
  width:24px;
  height:24px;
  border:1px solid rgba(255,255,255,.2);
  border-radius:2px;
  background:rgba(255,255,255,.08);
  box-shadow:inset 0 1px rgba(255,255,255,.16);
  transition:background .25s,border-color .25s;
}
.ctl-icon svg{
  display:block;
  width:14px;
  height:14px;
  transition:transform .3s var(--ease);
}
.room-exit-button,
.projects-close-button{
  gap:10px !important;
  letter-spacing:.07em;
}
/* the well has to invert with the button, which fills mint on hover */
.room-exit-button:hover .ctl-icon,
.room-exit-button:focus-visible .ctl-icon,
.projects-close-button:hover .ctl-icon,
.projects-close-button:focus-visible .ctl-icon{
  border-color:rgba(0,0,0,.24);
  background:rgba(0,0,0,.12);
}
.room-exit-button:hover .ctl-icon svg,
.room-exit-button:focus-visible .ctl-icon svg,
.projects-close-button:hover .ctl-icon svg,
.projects-close-button:focus-visible .ctl-icon svg{transform:translateX(-2px)}

/* the plate keeps its light fill -- it is the one bright accent in the header
   -- but takes the system's corner and a real icon instead of a character */
.exit-icon{
  border-radius:2px !important;
  transition:transform .3s var(--ease);
}
.exit-icon svg{display:block;width:18px;height:18px}
.exit-link:hover .exit-icon,
.exit-link:focus-visible .exit-icon{transform:translateX(2px)}

/* The close buttons are 30px tall, too short to hold a 24px well with any air.
   Scoped to a FINE pointer: written unscoped with !important it beat the
   coarse-pointer block that grants them 44px, and measured on a touch viewport
   they came back 38px -- a tap target regression introduced by the fix. */
@media (pointer:fine){
  .projects-close-button{min-height:38px !important;padding:0 13px !important}
}

/* ══ THE NAV CONTROLS SIT SMALLER ══════════════════════════════════════════
   Asked for: they read too big. They were sized by the tap target -- 44px on a
   coarse pointer -- so the visible box was as tall as the finger needed, which
   is the wrong way round. The box shrinks and the TARGET stays 44px, carried by
   a transparent ::after that extends past the visible edge. Both are true at
   once: it looks like a 32px control and taps like a 44px one.

   ::after is free on all four -- checked, none of them had one. Anchoring:
   .room-exit-button is position:fixed and .projects-close-button is absolute
   above 720px, so both already establish a containing block; .exit-link is a
   static grid item and the close button goes static below 720px, so those two
   are the only ones that need position:relative, and the exit button must NOT
   be given it -- it is fixed, and relative would drop it back into flow. */
.room-exit-button{
  min-height:32px !important;
  padding:0 11px !important;
  gap:8px !important;
  font-size:9px !important;
}
.projects-close-button{
  min-height:30px !important;
  padding:0 10px !important;
  gap:8px !important;
}
.ctl-icon{
  flex:0 0 20px;
  width:20px;
  height:20px;
}
.ctl-icon svg{width:12px;height:12px}

.exit-link{padding:5px 10px !important}
.exit-icon{width:26px !important;height:26px !important;flex:0 0 26px}
.exit-icon svg{width:14px;height:14px}
.exit-copy b{font-size:9px}
.exit-copy small{font-size:6px}

/* On a coarse pointer they keep a little more height than the desktop -- but
   36px, not the 44px they were. Two attempts before this one, both recorded
   because the first looked like it worked:

   - min-height:0 plus a transparent 44px ::after to carry the target. The
     ::after never registered; measured by scanning outward from the centre with
     elementFromPoint, the target came back equal to the VISIBLE box. And the
     min-height:0 -- later in the file than the 32px floor above, both important
     -- won, collapsing the phone controls to 22px.
   - So the target is the box again, at a number that is actually small.

   36px is above the 24px WCAG 2.2 AA minimum (2.5.8) with half again to spare.
   It is below the 44px AAA figure (2.5.5) this file had been holding
   everywhere else, which is a deliberate trade for the smaller look. */
@media (pointer: coarse){
  .room-exit-button,
  .projects-close-button{
    min-height:36px !important;
    display:inline-flex;
    align-items:center;
  }
  .exit-link{min-height:38px !important}
}

/* The header is a grid and .exit-link only set justify-self, so align-self was
   still stretch -- the link was taking the row's full height whatever its
   padding said. Measured 48px against a 38px content box; this is what makes
   the shrink actually land on the desktop. */
.exit-link{align-self:center}

/* product-premium.css:44 pins .premium-exit at min-height:48px !important with
   a 176px minimum width and 14px of left padding -- which is why the padding
   and align-self above changed nothing on the desktop; the height was a floor,
   not the content. Same file forces 48x48 below 820px. Both are answered here.
   The minimum widths go too: a control that hugs its label reads smaller than
   one held open to a fixed 176px, and that was half of "too big". */
.premium-exit{
  min-height:38px !important;
  min-width:0 !important;
  padding:5px 5px 5px 12px !important;
  gap:12px !important;
}
@media (max-width:820px){
  .premium-exit{min-width:38px !important;width:38px !important;padding:5px !important}
}
.room-exit-button{min-width:0 !important}

/* ── ...and narrower ───────────────────────────────────────────────────────
   Width on these is three things: the label, the tracking, and the chrome
   around it. Measured on the back control at 167px, the chrome was 50 of that
   -- 22px padding, a 20px well, an 8px gap -- and the label 117. So both halves
   get tightened: the chrome down to 43, and the label's own footprint via a
   smaller size and much tighter tracking, which on a 24-character string is
   worth more than the padding is. */
.room-exit-button{
  padding:0 9px !important;
  gap:7px !important;
  font-size:8.5px !important;
  letter-spacing:.03em !important;
}
.projects-close-button{
  padding:0 8px !important;
  gap:7px !important;
  font-size:8px !important;
  letter-spacing:.03em !important;
}
.ctl-icon{flex:0 0 18px;width:18px;height:18px}
.ctl-icon svg{width:11px;height:11px}

.premium-exit{
  padding:4px 4px 4px 10px !important;
  gap:9px !important;
}
.exit-icon{width:24px !important;height:24px !important;flex:0 0 24px}
.exit-icon svg{width:13px;height:13px}
.exit-copy small{letter-spacing:.1em}
.exit-copy b{font-size:8.5px}
@media (max-width:820px){
  .premium-exit{min-width:34px !important;width:34px !important;padding:5px !important}
}

/* The exit eyebrow is rgba(255,255,255,.46) at 6px. Composited against the
   button's near-black fill that is about 4.7:1 -- it passes, but on the wrong
   side of comfortable for the smallest type on the page, and it only looked
   fine at 17.57:1 because the scorer was ignoring the text's own alpha. */
.exit-copy small{color:rgba(255,255,255,.66) !important}

/* ── Smaller again, and this time it is the copy ───────────────────────────
   Three passes of chrome trimming had taken the padding, gap and wells about as
   far as they go: on the back control the label was 117 of 167px and, after the
   tracking change, still most of 146. So the labels shorten -- the arrow already
   says "back", the words only have to say where -- and the exit control drops to
   one line, which is what it does below 820px anyway. Its eyebrow was the taller
   of its two rows and no wider than the label under it, so removing it costs
   almost no width and a whole line of height.

     "Back to project corridor" -> "Back to corridor"
     "Back to the wings"        -> "Back to wings"

   Font sizes stay at 8-8.5px. Below that the labels stop being readable, which
   is a worse outcome than a slightly wider button. */
.exit-copy small{display:none}
.exit-copy{gap:0}
.exit-copy b{font-size:9px}
.premium-exit{
  min-height:32px !important;
  padding:4px 4px 4px 9px !important;
  gap:8px !important;
}
.exit-icon{width:22px !important;height:22px !important;flex:0 0 22px}
.exit-icon svg{width:12px;height:12px}
.room-exit-button{min-height:28px !important;padding:0 8px !important;gap:6px !important}
.projects-close-button{min-height:28px !important;padding:0 7px !important;gap:6px !important}
.ctl-icon{flex:0 0 16px;width:16px;height:16px}
.ctl-icon svg{width:10px;height:10px}
@media (max-width:820px){
  .premium-exit{min-width:32px !important;width:32px !important;padding:5px !important}
}
@media (pointer: coarse){
  /* the tap floor still applies; only the desktop goes to 28 */
  .room-exit-button,
  .projects-close-button{min-height:36px !important}
  .exit-link{min-height:36px !important}
}

/* ══ ICON ONLY ═════════════════════════════════════════════════════════════
   Asked for. The words go out of sight, NOT out of the document: an icon-only
   control's label is the only accessible name it has, so each keeps its text in
   a visually-hidden span rather than trading it for an aria-label. Same name to
   a screen reader, and it still works if this stylesheet never loads. Each also
   gets a title, because an icon alone tells a sighted first-time visitor
   nothing.

   The inner well goes too. With no label beside it, a bordered box inside a
   bordered box is just two rectangles -- the button itself becomes the frame
   and the icon sits in the middle of it. */
.ctl-label{
  position:absolute;
  width:1px;
  height:1px;
  margin:-1px;
  padding:0;
  overflow:hidden;
  clip-path:inset(50%);
  white-space:nowrap;
  border:0;
}
.room-exit-button,
.projects-close-button{
  width:28px;
  min-width:0 !important;
  padding:0 !important;
  gap:0 !important;
  justify-content:center;
}
.ctl-icon{
  flex:0 0 auto;
  width:auto;
  height:auto;
  border:0;
  background:none;
  box-shadow:none;
}
.ctl-icon svg{width:13px;height:13px}

/* the exit control is its plate now -- the dark shell around it was only ever
   there to hold the two lines of copy that are gone */
/* Clipped, not display:none. display:none takes it out of the accessibility
   tree, which is why this link had NO accessible name on a phone and needed an
   aria-label -- and that label then contradicted the markup text a checker can
   still see (WCAG 2.5.3). Clipping keeps the words as the name, the same way
   the back controls keep theirs, so no aria-label is needed at all. */
.exit-copy{
  position:absolute !important;
  width:1px !important;
  height:1px !important;
  margin:-1px !important;
  padding:0 !important;
  overflow:hidden !important;
  clip-path:inset(50%) !important;
  white-space:nowrap !important;
  border:0 !important;
}
.premium-exit{
  min-width:0 !important;
  width:auto !important;
  padding:0 !important;
  gap:0 !important;
  border-color:transparent !important;
  background:none !important;
  box-shadow:none !important;
}
.exit-icon{width:28px !important;height:28px !important;flex:0 0 28px}
.exit-icon svg{width:14px;height:14px}
@media (max-width:820px){
  .premium-exit{min-width:0 !important;width:auto !important;padding:0 !important}
}
@media (pointer: coarse){
  /* the box shrank to the icon, so the target has to be stated again */
  .room-exit-button,
  .projects-close-button{width:36px;min-height:36px !important}
  .exit-link{min-height:36px !important;min-width:36px}
  .exit-icon{width:32px !important;height:32px !important;flex:0 0 32px}
}

/* ══ THE WINGS FLOAT: THE BOARD GOES ═══════════════════════════════════════
   Asked for -- the big transparent square holding "Two wings." and the two
   cards is .corridor-map, and it comes off. The two wing cards stay: they are
   the info, they are controls, and they carry their own glass.

   Its padding stays. The fill and the edge are what was asked for, but the
   padding is what keeps the content off the screen edge, and dropping it would
   push the cards into the bezel.

   Legibility moves to .room-vignette, which is full-bleed and has no edge to
   read as a box -- the same substitution the website room uses. Weighted left
   and low where the copy is, lighter across the upper right so the corridor
   and the figures still carry the room. */
.product-wing-room.projects-open .corridor-map{
  background:none !important;
  border:0 !important;
  box-shadow:none !important;
  backdrop-filter:none !important;
  -webkit-backdrop-filter:none !important;
}
.product-wing-room.projects-open > .room-vignette{
  background:
    linear-gradient(180deg,
      rgba(8,9,12,.34) 0%,
      rgba(8,9,12,.60) 38%,
      rgba(8,9,12,.80) 76%,
      rgba(8,9,12,.88) 100%),
    linear-gradient(90deg,
      rgba(8,9,12,.48) 0%,
      rgba(8,9,12,.18) 62%,
      rgba(8,9,12,.04) 100%) !important;
}

/* The board's paragraph sits higher up the render on a phone than it does on
   the desktop, and that band is the lit part: measured 4.03:1 against a
   (104,97,89) backdrop with the board gone. #d0d2d6 needs the backdrop at about
   88 to clear 4.5, which from 104 at .60 alpha means about .68 -- so the
   mid-stop goes to .70 here and the desktop keeps its lighter gradient, where
   the same paragraph already reads 7.70:1. */
@media (max-width:1050px){
  .product-wing-room.projects-open > .room-vignette{
    background:
      linear-gradient(180deg,
        rgba(8,9,12,.40) 0%,
        rgba(8,9,12,.70) 38%,
        rgba(8,9,12,.84) 76%,
        rgba(8,9,12,.9) 100%),
      linear-gradient(90deg,
        rgba(8,9,12,.48) 0%,
        rgba(8,9,12,.18) 62%,
        rgba(8,9,12,.04) 100%) !important;
  }
}

/* ══ THE APP CARD: ICON BESIDE THE NAME ════════════════════════════════════
   Asked for -- a bigger icon with the name alongside it rather than stacked
   under it. The card was a flex column, which can only stack, so it becomes a
   two-column grid: the icon takes the first column of row 2 and the name the
   second, and everything else spans both. That keeps the source order and the
   accessible name exactly as they were -- no wrapper div, no reordering.

   The <br> in "Finance<br>Tracker" stays. Beside a 76px icon the two short
   lines balance it; on one line the name would be a long thin strip next to a
   tall square. */
.wing-apps .room-door{
  display:grid !important;
  grid-template-columns:auto minmax(0,1fr);
  align-content:start;
  gap:12px 16px;
}
.wing-apps .room-door>span,
.wing-apps .room-door>p,
.wing-apps .room-door>i,
.wing-apps .room-door>em{grid-column:1 / -1}
.wing-apps .room-door>span,
.wing-apps .room-door>i{justify-self:start}
.wing-apps .room-door>img{
  grid-column:1;
  grid-row:2;
  align-self:center;
  width:76px !important;
  height:76px !important;
}
.wing-apps .room-door>b{
  grid-column:2;
  grid-row:2;
  align-self:center;
  font-size:clamp(30px,3.4vw,40px) !important;
  line-height:1;
  letter-spacing:-.045em;
}
@media (max-width:520px){
  /* a 76px square plus a 40px name will not both fit a 330px card */
  .wing-apps .room-door{gap:10px 13px}
  .wing-apps .room-door>img{width:64px !important;height:64px !important}
  .wing-apps .room-door>b{font-size:30px !important}
}

/* The board's paragraph reads 4.34:1 in the apps-wing state on a phone -- the
   render behind it differs from the choice state, where the same line measures
   5.25:1. Rather than darken the vignette further, which works against showing
   the room at all, the type comes up: at 208 against a 94 backdrop the ratio is
   4.20, and 215 is the break-even, so 224 clears it with margin. */
.corridor-heading p{color:#e0e2e6}

/* ── The founder's face, cut off by the portrait crop ───────────────────────
   The two sources put the head in completely different places: the landscape
   render has it at y 0.225-0.385, safely mid-frame, while the portrait render
   has it at y 0.038-0.112 -- hard against the top edge. object-fit:cover
   centres its crop, so on any viewport that picks the portrait source and is
   not tall enough to need all of it, the head is the first thing cropped away.

   Measured before touching it: 26px lost at 360x640, 16px at 411x757, 226px at
   768x1024 and 508px at 984x875 -- the head entirely gone. Only 390x844, the
   tallest of them, kept it.

   Solving for object-position: the head top must land at or below the frame,
   which needs py <= (0.038*dh - margin) / (dh - bh). Worst case is 984x875 at
   0.047, then 768x1024 at 0.062. 3% clears both with room, and is scoped to
   exactly the query that selects the portrait source so the landscape framing
   is untouched. */
@media (max-aspect-ratio: 13/10){
  .executive-room .room-bg{object-position:50% 3%}
}

/* ══ DESKTOP CONTRAST: THE CORRIDOR ROOMS ══════════════════════════════════
   A full audit of all 11 states across 8 viewports: the phone and every small
   size are clean, and 31 failures cluster on the desktop sizes. One cause --
   the phone's vignettes and panel fills were tuned through this session while
   the desktop kept lighter ones, and the renders are brightest exactly where
   the corridor copy sits.

   Three different fixes, because three different things are wrong, and which
   one applies is decided by arithmetic rather than by taste:

   1. WHERE NO COLOUR CAN WIN, DARKEN THE GROUND. .room-copy p measured 2.59:1
      against a (121,116,116) backdrop. That backdrop is luminance .178, so
      4.5:1 needs a text luminance of .976 -- brighter than white, which does
      not exist. Pure white would reach 4.15. So the vignette has to carry it.

   2. WHERE THE PANEL IS TRANSLUCENT, GIVE IT A GROUND. The room sign measured
      1.78:1 in the Finance room, over a bright teal patch reading (101,129,121)
      -- luminance .199, needing 1.068. Also impossible. These are glass panels
      over a render that is sometimes bright; on the desktop they carry more of
      their own fill.

   3. WHERE THE BACKDROP IS DARK AND THE TYPE IS DIM, BRIGHTEN THE TYPE. The
      header's "Project Office" at #898b91 over (61,48,53) computes 3.69:1, and
      4.5 needs 155; 176 gives 5.79 with margin. That one is just a dim grey. */
@media (min-width:1051px){
  .tour-room:not(.product-room):not(.website-room):not(.product-wing-room) > .room-vignette{
    background:
      linear-gradient(90deg,rgba(10,11,13,.94) 0%,rgba(10,11,13,.74) 42%,rgba(10,11,13,.30) 72%),
      linear-gradient(0deg,rgba(10,11,13,.80),rgba(10,11,13,.18) 48%);
  }
  .room-sign,
  .speech,
  .manager-label,
  .room-metric{
    background:linear-gradient(135deg,
      rgba(24,25,30,.90),
      rgba(15,16,20,.93) 58%,
      rgba(11,12,16,.94)) !important;
  }
  .room-sign small,
  .room-metric span{color:#c9cbd0 !important}
  .room-copy p,
  .final-copy p{color:#dcdde1 !important}
}
/* the header subtitle is dim at every width, not just the desktop */
.tour-brand>span:last-child{color:#b0b2b8}
/* dark label on the light entry button: 4.18:1, and it is the TYPE that is
   light here, not the ground */
.projects-entry-button small{color:#2d2f35 !important}

/* The last four, and the header is three of them. On the desktop .tour-header
   is a LIGHT glass tint, so in a room whose render is bright behind it the bar
   itself goes bright: the status text measured 1.78:1 in the Finance room
   against a (101,129,121) backdrop. Luminance .199 needs a text luminance of
   1.068 for 4.5:1, so again no colour wins and the bar needs its own ground --
   the same dark fill it already uses below 1051px. */
@media (min-width:1051px){
  .tour-header{
    background:linear-gradient(135deg,
      rgba(22,23,28,.90),
      rgba(14,15,19,.93) 58%,
      rgba(11,12,16,.94)) !important;
  }
  .tour-status span{color:#c9cbd0 !important}
}
/* two that were only just short: 4.31 and 4.50 against 4.5 */
.product-title small{color:#c2c4c9 !important}
.wing-idea>i{color:#bcbec3 !important}

/* ══ THE SCREENSHOT STRIP AND ITS VIEWER ═══════════════════════════════════
   The display box is gone; the screens live in the story card now, at the right
   end of the actions row -- the empty half of that line -- and open full size
   on a tap.

   margin-left:auto is what puts them in that empty space, and flex-wrap means
   that when the row runs out of width (which it does on a phone, where the two
   buttons already take 243 of 371) the strip drops to its own right-aligned
   line rather than shoving the buttons. */
.shot-strip{
  display:flex;
  gap:5px;
  margin-left:auto;
  align-items:center;
}
.shot-thumb{
  display:block;
  width:38px;
  padding:0;
  overflow:hidden;
  border:1px solid rgba(255,255,255,.34);
  border-radius:2px;
  background:rgba(11,13,16,.6);
  cursor:pointer;
  transition:border-color .25s,transform .25s var(--ease);
}
.shot-thumb img{display:block;width:100%;height:auto}
.shot-thumb:hover,
.shot-thumb:focus-visible{border-color:var(--mint);transform:translateY(-2px)}
.shot-thumb:focus-visible{outline:2px solid var(--mint);outline-offset:3px}
@media (pointer: coarse){
  /* 38px is under the 24px AA minimum in neither direction, but a 38x76 target
     is thin for a thumb; the strip gets a little more width on touch */
  .shot-thumb{width:44px}
}

/* the viewer itself */
.lb[hidden]{display:none !important}
.lb{
  position:fixed;
  z-index:2000;
  inset:0;
  display:grid;
  grid-template-columns:auto 1fr auto;
  grid-template-rows:1fr auto;
  align-items:center;
  justify-items:center;
  gap:14px;
  padding:clamp(14px,4vw,40px);
  background:rgba(6,7,10,.93);
}
.lb-img{
  grid-column:2;
  grid-row:1;
  max-width:min(100%,520px);
  max-height:88svh;
  width:auto;
  height:auto;
  border:1px solid rgba(255,255,255,.16);
  border-radius:2px;
  box-shadow:0 40px 120px rgba(0,0,0,.6);
}
.lb-cap{
  grid-column:1/-1;
  grid-row:2;
  margin:0;
  color:#dcdee2;
  font-size:10px;
  font-weight:600;
  letter-spacing:.1em;
  text-transform:uppercase;
}
.lb-x{
  position:absolute;
  right:clamp(12px,3vw,26px);
  top:clamp(12px,3vw,26px);
}
.lb-x,
.lb-nav{
  display:grid;
  place-items:center;
  width:40px;
  height:40px;
  padding:0;
  color:#f4f4f5;
  border:1px solid rgba(255,255,255,.3);
  border-radius:2px;
  background:rgba(18,20,25,.9);
  cursor:pointer;
  transition:color .25s,border-color .25s,background .25s;
}
.lb-x svg,
.lb-nav svg{display:block;width:17px;height:17px}
.lb-prev{grid-column:1;grid-row:1}
.lb-next{grid-column:3;grid-row:1}
.lb-x:hover,.lb-x:focus-visible,
.lb-nav:hover,.lb-nav:focus-visible{
  color:#17181b;
  border-color:var(--mint);
  background:var(--mint);
}
@media (max-width:560px){
  /* the arrows move under the image rather than squeezing its width */
  .lb{grid-template-columns:1fr;grid-template-rows:1fr auto auto}
  .lb-img{grid-column:1;max-height:70svh}
  .lb-cap{grid-row:2}
  .lb-prev,.lb-next{grid-row:3;grid-column:1}
  .lb-prev{justify-self:start}
  .lb-next{justify-self:end}
}

/* Measured: the strip's right edge landed at 594 against a card that ends at
   523 -- 71px outside it. The two buttons take about 250 of the row and the six
   thumbs another 253, which does not fit in 523, and .product-actions only
   carried flex-wrap below 720px. With wrapping it drops to its own line and
   margin-left:auto keeps it at the right, which is where it was asked for. */
.product-actions{flex-wrap:wrap;row-gap:12px}

/* ══ THE SCREENSHOTS TAKE THE EMPTY BAND ═══════════════════════════════════
   .product-actions carries margin-top:auto, so it is pinned to the foot of the
   card and ALL of the card's slack collects in one gap above it -- measured at
   roughly 215px on the desktop, doing nothing. The strip sat below the buttons
   at 44px while that band sat empty above.

   As a sibling before the actions with flex:1, the strip takes that slack
   instead of the gap taking it, and the thumbs are sized by HEIGHT so they grow
   into whatever the card has spare. flex:1 1 0 on each thumb splits the width
   evenly, min-width:0 lets them shrink below their intrinsic size, and cover
   from the top keeps each screen recognisable rather than letterboxed. */
.product-story>.shot-strip{
  flex:1 1 auto;
  min-height:96px;
  margin:16px 0 0;
  margin-left:0;
  align-items:stretch;
  gap:6px;
}
.product-story>.shot-strip .shot-thumb{
  flex:1 1 0;
  width:auto;
  min-width:0;
  height:auto;
}
.product-story>.shot-strip .shot-thumb img{
  width:100%;
  height:100%;
  object-fit:cover;
  object-position:50% 0;
}
@media (pointer: coarse){
  /* the width rule from the old strip no longer applies now that they flex */
  .product-story>.shot-strip .shot-thumb{width:auto}
}

/* With the display box gone the suite still had TWO columns on the desktop --
   .78fr for the story and 1.22fr for a child that no longer exists -- so 60% of
   the card was empty glass. Same fault the website room had when its screenshot
   was removed, and the same fix: one column, and a card that hugs it rather
   than being held open at min(88vw,1400px). The screenshots get that width. */
@media (min-width:1051px){
  .finance-product .product-suite{
    grid-template-columns:1fr !important;
    width:min(62vw,880px) !important;
  }
}

/* The new finance render is brighter than the one it replaced -- lit faces, a
   glowing wall dashboard, warm lamps -- and the card is glass, so the copy on it
   went with the room: the feature list fell to 2.95:1 and the paragraph to
   3.53:1 against backdrops reaching (118,104,95). #bfc0c4 needs its backdrop
   under about 76 to clear 4.5, so the card carries more of its own fill on the
   desktop, the way it already does on a phone. The phone was unaffected: its
   .product-story is already at ~.69 from the earlier pass. */
@media (min-width:1051px){
  .finance-product .product-story{
    background:linear-gradient(135deg,
      rgba(22,23,28,.88),
      rgba(14,15,19,.91) 58%,
      rgba(10,11,15,.93)) !important;
  }
}

/* ══ THE FINANCE ROOM'S CARD GOES, THE COPY FLOATS ═════════════════════════
   Asked for, and the same move the website room already made. The card was
   doing real work here -- it had just been firmed to .88 because the new render
   is bright enough to sink the feature list to 2.95:1 -- so removing it hands
   that job to .room-vignette, which is full-bleed and has no edge to read as a
   box.

   Weighted left and low, where the copy is, and lighter across the upper right
   so the three figures and the wall dashboard still carry the room. The
   screenshot strip is unaffected: each thumb has its own border and fill. */
@media (max-width:1050px){
  .finance-product .product-suite,
  .finance-product .product-story{
    background:none !important;
    border:0 !important;
    box-shadow:none !important;
    backdrop-filter:none !important;
    -webkit-backdrop-filter:none !important;
  }
  .finance-product .product-story{
    text-shadow:0 2px 12px rgba(0,0,0,.92),0 1px 3px rgba(0,0,0,.88);
  }
  .finance-product > .room-vignette.strong{
    background:
      /* The copy starts at ~15% of the screen now that the card is derived
         from its own height, so the band above it no longer has to be dark --
         that band is the only place the render is visible at all. */
      linear-gradient(180deg,
        rgba(8,9,12,.12) 0%,
        rgba(8,9,12,.28) 13%,
        rgba(8,9,12,.66) 30%,
        rgba(8,9,12,.86) 72%,
        rgba(8,9,12,.92) 100%),
      linear-gradient(90deg,
        rgba(8,9,12,.50) 0%,
        rgba(8,9,12,.20) 60%,
        rgba(8,9,12,.05) 100%) !important;
  }
}

/* The two buttons now live inside .product-title, beside the name. They arrive
   carrying margin-top:auto from their old life at the foot of the card, which
   inside a title row would shove them to its bottom edge, so that is reset.
   margin-left:auto puts them at the right of the row; wrapping lets them drop
   under the name on a phone, where 411px leaves only about 100px after the icon
   and the title -- still grouped with the name rather than stranded. */
.finance-product .product-title{
  flex-wrap:wrap;
  align-items:center;
  row-gap:12px;
  column-gap:16px;
}
.finance-product .product-title .product-actions{
  flex:0 0 auto;
  width:auto;
  margin:0 0 0 auto !important;
}
/* the strip is the last child now, so its flex:1 takes the slack at the FOOT of
   the card instead of in the middle -- the screenshots end the room */
.finance-product .product-story>.shot-strip{margin-bottom:0}

/* The reveal was a constant -- 40svh -- and what is wanted is "render above,
   content ending at the foot", which depends on how tall the content is, not on
   the viewport alone. Measured across four phone sizes the card is 575-646px
   tall while viewports run 640-1024, so the offset is derived from that:
   100svh minus the card, with a floor so the smallest phone still gets a strip
   of room rather than a negative margin.

     360x640  ->  24px of render   (the card is 608 of a 640 screen; there is
                                    not more to give without cutting copy)
     411x757  -> 117px
     390x844  -> 204px
     768x1024 -> 384px

   flex-end was tried first and was wrong: the room's content already overflows
   100svh, so margin-top:auto resolved to 0 and the card jumped to the TOP with
   the motion track filling the space below it. */
@media (max-width:1050px){
  .finance-product .product-suite{
    top:auto !important;
    margin-top:max(24px, calc(100svh - 640px)) !important;
    margin-bottom:0 !important;
  }
}

/* ══ THE PROJECT FLOOR'S BACK CONTROLS ═════════════════════════════════════
   Two faults, both measured.

   1. THE WAY BACK WAS OFF-SCREEN. .wing-back is the last item in .wing-apps, so
      once the two cards stack on a phone it sat at y=807 in a 757px viewport --
      open the apps wing and the control that leaves it is below the fold. It
      leads the pane now instead of ending it.

   2. TWO IDENTICAL ARROWS, DIFFERENT DESTINATIONS. Since the controls became
      icon-only, "back to entrance" and "back to wings" are the same glyph, and
      in the apps wing both were on screen at once with nothing to tell them
      apart. Only one step back is ever meaningful, so while the apps wing is
      open the entrance control stands down and .wing-back is the only arrow.

   And on a phone the entrance control was static under the paragraph, reading
   as a stray square in the middle of the copy; it takes the heading's top-right
   corner, the same place it holds on the desktop. */
.wing-apps .wing-back{order:-1;margin:0 0 4px}
.corridor-map:has(.wing-apps:not([hidden])) [data-close-projects]{display:none}
@media (max-width:720px){
  .corridor-heading{position:relative}
  .corridor-heading [data-close-projects]{
    position:absolute !important;
    right:0 !important;
    top:0 !important;
    margin-top:0 !important;
  }
}

/* ── The wings back control, properly placed ────────────────────────────────
   Leading the pane fixed its reachability but left it stranded: a lone square
   on its own row under the heading's divider, with the whole width empty beside
   it. And it reads as flat black because .projects-close-button is a .06 white
   wash -- over the dark shoulder of the render behind it, that is simply black.

   It takes the heading's top-right corner instead -- the exact spot the
   entrance control occupies in the other state, so the arrow never moves
   between the two panes -- and both get the glass the rest of the icon controls
   use: a translucent fill, a hairline edge and an inset highlight, so they read
   as the same family rather than as boxes dropped on the image. */
.corridor-heading .wing-back{
  position:absolute !important;
  right:0 !important;
  top:0 !important;
  left:auto !important;
  margin:0 !important;
  z-index:3;
}
/* It lives in the heading now, so it is no longer hidden by .wing-apps[hidden]
   the way it was as a descendant -- it has to be told directly which state it
   belongs to, or a "back to wings" arrow sits on the wings screen itself. */
.corridor-map:has(.wing-apps[hidden]) .wing-back{display:none !important}
.projects-close-button{
  border-color:rgba(255,255,255,.34) !important;
  background:linear-gradient(135deg,
    rgba(46,49,58,.52),
    rgba(14,15,20,.58) 58%,
    rgba(10,11,15,.6)) !important;
  box-shadow:
    inset 0 1px rgba(255,255,255,.22),
    0 10px 26px rgba(0,0,0,.34) !important;
}
.projects-close-button:hover,
.projects-close-button:focus-visible{
  border-color:var(--mint) !important;
  background:var(--mint) !important;
}


/* ── The scroll flicker, measured on an S10+ ──────────────────────────────
   products.css:73 puts a filter on the ACTIVE room's full-screen background
   whenever body.is-walking is set, and products.js:437 sets that class on
   every scroll event and clears it 130ms after scrolling stops.

   On a phone that reads as a flicker rather than motion, because it is one:
   start scrolling and a blur appears across the whole backdrop, pause and it
   snaps off, scroll again and it returns. Twice a second while reading.

   It is also the most expensive thing on the page. Real-touch frame capture
   through the projects floor: 13 frames over 33ms and 3 over 50ms, worst
   70.8ms, all clustered where the DOM is heaviest.

   Neither rule in products.css carries a media query, so both reach mobile.
   The blur goes below 1051px and the camera grade stays -- brightness and
   saturate cost nothing and carry the same intent. This matches how
   backdrop-filter is already scoped in this file (18 other places).
   Desktop keeps the blur; a pointer there is not holding the screen 30cm
   from its face. */
@media (max-width:1050px){
  body.is-walking .tour-room.is-active .room-bg{
    filter:brightness(var(--camera-brightness)) saturate(var(--camera-saturate));
  }
}

/* ── Habit Kramedha room: real screenshots, given the room's empty half ──────
 *
 * The room first copied the Finance layout, which puts the shot-strip inside
 * .product-story. That column is 373px wide, so five 1:2 thumbs landed at
 * about 50px each — too small to read a single word of the interface — while
 * the whole right half of the room held a decorative CSS habit-board and a
 * floating icon and nothing else.
 *
 * So the strip moved into .product-display and the mockups went. They were
 * standing in for screenshots nobody had; with the real screens here, a drawn
 * grid of grey dots is a worse version of the Monthly tracker screenshot
 * sitting next to it.
 *
 * Scoped to .habit-product throughout: the Finance room keeps its own layout
 * untouched, and this must not become a second definition of .shot-strip.
 * Three up, two below, centred — five across one row would force each back
 * under 110px, which is where the charts stop being legible. */
.habit-product .product-display{
  display:flex;
  flex-direction:column;
  align-items:center;
  justify-content:center;
  gap:22px;
  padding:34px 28px;
}
.habit-product .product-display .shot-strip{
  /* Above .product-display::before, which is an inset:0 gradient at z-index:1
     and therefore paints OVER the panel's own children. Across the big dark
     screenshots it is imperceptible; across a 9px caption it washed the lower
     half of every word away, which read exactly like the text being clipped.
     elementsFromPoint is what gave it away — at the caption's baseline the
     top of the stack was .product-display, not the thumb. */
  position:relative;
  z-index:2;
  display:flex;
  /* never shrink. As a flex item of the column above it the strip defaults to
     flex-shrink:1, so inside the phone's 306px panel its box settled at 200px
     while its own 222px thumbs stayed put — and because overflow-x:auto forces
     overflow-y to a non-visible value, the strip then clipped them. The
     screenshots were the right size and the box around them was not. */
  flex:0 0 auto;
  flex-wrap:wrap;
  align-content:center;
  justify-content:center;
  gap:14px;
  width:100%;
  max-width:560px;
  margin:0;
  overflow:visible;
}
/* No frame. The base .shot-thumb draws a 1px white-ish border and a dark
   fill, which around a dark screenshot reads as a box drawn on top of the
   picture rather than an edge belonging to it. The screens are their own
   object: rounded corners so the artwork is shaped, a shadow to lift them off
   the room, and nothing else. */
.habit-product .product-display .shot-thumb{
  flex:0 0 auto;
  display:flex;
  flex-direction:column;
  align-items:center;
  gap:9px;
  width:150px;
  padding:0;
  border:0;
  background:none;
  overflow:visible;
  cursor:pointer;
  transition:transform .22s cubic-bezier(.23,1,.32,1), filter .22s ease;
}
.habit-product .product-display .shot-thumb img{
  display:block;
  width:100%;
  height:auto;          /* the asset is 1:2; let it keep its own ratio */
  border-radius:14px;
  box-shadow:0 14px 34px rgba(0,0,0,.42);
}
/* The screen's own name, under it. Taken from data-cap so it cannot drift
   from the caption the viewer sees in the lightbox. */
.habit-product .product-display .shot-thumb::after{
  content:attr(data-cap);
  font:600 10px/1.2 var(--font-interface, system-ui, sans-serif);
  letter-spacing:.08em;
  text-transform:uppercase;
  color:rgba(236,239,237,.62);
  white-space:nowrap;
  /* The caption lands exactly where the room's dark backdrop gives way to the
     panel's grey, so each word is lit differently top and bottom — it reads as
     a word sliced in half even though both halves clear AA (6.22 and 4.72
     measured on the device). The shadow carries it across the seam. */
  text-shadow:0 1px 3px rgba(0,0,0,.75);
}
.habit-product .product-display .shot-thumb:hover,
.habit-product .product-display .shot-thumb:focus-visible{
  transform:translateY(-6px);
}
.habit-product .product-display .shot-thumb:hover::after,
.habit-product .product-display .shot-thumb:focus-visible::after{
  color:rgb(var(--suite-accent));
}
.habit-product .product-display .shot-thumb:hover img,
.habit-product .product-display .shot-thumb:focus-visible img{
  box-shadow:0 22px 48px rgba(0,0,0,.52);
}
.habit-product .product-display .manager-label{
  position:static;
  align-self:center;
}

/* Narrow: ONE scrollable row, not a wrapped grid.
 *
 * Measured on the S10+ against the live page: once the suite stacks, the
 * display panel is 306px tall with overflow:hidden and a 333px cap, while the
 * wrapped two-row strip stood 379px. The first row was clipped above the panel
 * and the last below it — the top screenshot began mid-form at "Optional
 * emoji" and the bottom one was cut at "Next badge". Nothing about that is
 * visible in the markup or in a width-only check; it needed the real device
 * and the real panel height.
 *
 * Shrinking to fit would have put the thumbs near 63px, below the point where
 * the charts and the calendar read as anything. A single row at 112px is
 * 224px tall, inside the panel, and simply scrolls — which is what a phone
 * expects of a row of screenshots anyway. Snap points so it settles on a
 * screen rather than between two. */
@media (max-width:1050px){
  /* 104px, not 112: the captions added 16px of height and the strip went to
     246px against a 236px content area, so it started clipping again. A 1:2
     thumb costs two pixels of height for every one of width, which is why
     this budget has to be checked against the panel every time anything is
     added under the screenshots. 104 -> 208 + 7 gap + 11 caption = 226. */
  .habit-product .product-display{padding:14px 0;gap:12px}
  .habit-product .product-display .shot-strip{
    align-self:stretch;
    flex-wrap:nowrap;
    justify-content:flex-start;
    gap:12px;
    max-width:none;
    /* 8px of head-room, not 2: shotFloat lifts each image 5px and
       overflow-x:auto forces overflow-y to clip, so the strip sliced
       the top off every screenshot as it rose. */
    padding:8px 16px 6px;
    overflow-x:auto;
    overscroll-behavior-x:contain;
    scroll-snap-type:x proximity;
    -webkit-overflow-scrolling:touch;
    scrollbar-width:none;
  }
  .habit-product .product-display .shot-strip::-webkit-scrollbar{display:none}
  /* 100px, for slack rather than for size. At 104 the thumb stood 226px and
     the strip's content box was also exactly 226 — no margin at all — and
     because overflow-x:auto forces overflow-y to clip, sub-pixel rounding ate
     the caption, the last thing in the box. It left a 1px light line where
     the word should be: present in the DOM, 10.8px tall by computed style,
     painting (a forced red proved it), and shaved to nothing on the glass.
     100 -> 200 + 7 + 11 = 218 against 226, so there are 8px to lose. */
  .habit-product .product-display .shot-thumb{
    width:100px;
    gap:7px;
    scroll-snap-align:center;
  }
  .habit-product .product-display .shot-thumb img{border-radius:11px}
  .habit-product .product-display .shot-thumb::after{font-size:9px}
  /* The "Project room / Wellbeing suite" plate goes on narrow screens. With
     it the panel wanted 303px of a 276px box, which clipped 11px off the top
     of every screenshot and 13px off the plate itself. It is the one thing
     here that repeats: the room header directly beneath it already reads
     ROOM 02, and the suite is named in the room's own copy. */
  .habit-product .product-display .manager-label{display:none}
  /* no lift on a touch screen: there is no hover to leave, so the transform
     sticks after a tap and the thumb sits raised until something redraws */
  .habit-product .product-display .shot-thumb:hover{transform:none}
}

/* ── Habit Kramedha: the three story lines, and the Play badge ──────────────
 *
 * Numbered because they are a sequence — open it, mark a day, read the trend
 * — not three unrelated features. Every line is checked against the
 * screenshots beside them: the quote card and the done/left ring on Today,
 * "Tap a square to mark that day done" on Monthly tracker, the mood and
 * energy buttons on Today, and the 7/30/90-day range with the by-weekday
 * chart on Insights. Nothing here claims anything the app does not do. */
.product-room .story-steps{
  margin:14px 0 0;
  padding:0;
  list-style:none;
  counter-reset:hkstep;
  display:grid;
  gap:9px;
}
/* These are sentences, not pills. The room's .product-story li is styled as a
   bordered uppercase chip, which this list inherited wholesale — boxed,
   letter-spaced and shouting. Everything that makes a chip is undone here. */
.product-room .story-steps li{
  position:relative;
  margin:0;
  padding:0 0 0 25px;
  border:0;
  background:none;
  text-transform:none;
  letter-spacing:normal;
  font:300 13px/1.5 var(--font-interface, system-ui, sans-serif);
  color:var(--suite-ink-2, rgba(236,239,237,.74));
}
.product-room .story-steps li::before{
  counter-increment:hkstep;
  content:counter(hkstep);
  position:absolute;
  left:0;
  top:1px;
  display:grid;
  place-items:center;
  width:17px;
  height:17px;
  border:1px solid rgba(var(--suite-accent),.4);
  border-radius:50%;
  color:rgb(var(--suite-accent));
  font-size:9.5px;
  font-weight:600;
}

/* NOT a link, deliberately. The store page for com.lyvorianstudio.habitaroha
 * returns 404 today — the app is in closed testing. A badge that 404s is
 * worse than one that says what is true, and Google's own badge guidance is
 * that it should open a listing.
 *
 * WHEN IT GOES LIVE: swap the <span class="play-soon"> for the same
 * <a class="dl-badge"> the Finance room uses, pointing at
 * https://play.google.com/store/apps/details?id=com.lyvorianstudio.habitaroha
 * and drop the <b>. Nothing else here needs to change. */
.habit-product .play-soon{
  display:inline-flex;
  align-items:center;
  gap:10px;
}
.habit-product .play-soon img{
  width:104px;
  height:auto;
  opacity:.55;
  filter:grayscale(.65);
}
.habit-product .play-soon b{
  font:600 9.5px/1 var(--font-interface, system-ui, sans-serif);
  letter-spacing:.11em;
  text-transform:uppercase;
  color:rgba(var(--suite-accent),.92);
  white-space:nowrap;
}
@media (max-width:1050px){
  .product-room .story-steps li{font-size:12.5px;padding-left:24px}
  .habit-product .play-soon img{width:92px}
}

/* The three feature claims as one line rather than three bordered chips.
   The chips cost ~110px of a column that was already 127px over its own
   height — which is what pushed the actions, and with them the Play badge,
   out of the visible panel entirely. Same three facts, same wording as the
   product card, a fifth of the space. */
.product-room .room-facts{
  margin:13px 0 0;
  font:400 11px/1.5 var(--font-interface, system-ui, sans-serif);
  letter-spacing:.055em;
  text-transform:uppercase;
  color:rgba(236,239,237,.5);
}

/* The name, explained. Set quieter than the lead so it reads as a note on the
   title rather than as the pitch.
   Kramedha is NOT a dictionary word — it is the studio's own compound, so the
   line says so plainly and names the two roots it is built from: krama
   (क्रम) step-by-step progress, and medha (मेधा) wisdom. An earlier draft of
   this line had the second root as edh, "to grow"; that was wrong, and it was
   wrong in a way no checker would have caught. */
.product-room .room-note{
  margin:9px 0 0;
  font:300 12px/1.5 var(--font-interface, system-ui, sans-serif);
  color:rgba(236,239,237,.52);
}
.product-room .room-note i{font-style:italic;color:rgba(236,239,237,.74)}
@media (max-width:1050px){
  .product-room .room-note{font-size:11.5px}
}

/* ── Habit Kramedha: the title block, and the meaning line as the lead ──────
 *
 * The meaning line is the point of this room, not a footnote to it: it says
 * what the product is called and why. It was set at 52% grey under the lead
 * paragraph, which buried it. It now reads brighter than the body copy, the
 * two roots carry the suite accent, and a hairline on the left marks it as
 * the thing to read first.
 *
 * The icon grows from 70px to 108px and the name shifts right off it, so the
 * mark reads as the product's own rather than as a bullet beside the words.
 * All scoped to .habit-product — the Finance room keeps its proportions. */
.habit-product .product-title{gap:24px}
.habit-product .product-title img{
  width:96px;
  height:96px;
  flex:0 0 auto;
  border-radius:24px;
  box-shadow:0 22px 46px rgba(0,0,0,.42), 0 0 0 1px rgba(var(--suite-accent),.16);
}
/* The name must not break mid-word.
 *
 * .product-title h2 carries overflow-wrap:anywhere, which is right for a
 * heading that must never push the page wide — but "Kramedha" is a single
 * long token, and at 108px the icon left the text column 156px against the
 * 158px the word needed. It split as "Habit / Kramedh / a". Two pixels.
 *
 * Two things fix it, and the font size is NOT one of them: the size here is
 * set by a clamp carrying !important at line 45, so any value written in this
 * block is dead. What works is the icon settling back to 96px — still half
 * again the 70px it was — which hands 12px back to the column, and forbidding
 * the mid-word break so the title wraps at its space instead. The word now
 * measures 158px in a 170px box.
 *
 * If a future name does outgrow the column it will overflow visibly rather
 * than quietly mangle itself, which is the failure worth having. */
.habit-product .product-title h2{
  margin-top:10px;
  overflow-wrap:normal;
  word-break:normal;
  hyphens:none;
}

/* ANDROID, as a badge that arrives. It used to be 8px grey lettering that
   read as a label on the heading; as a bordered pill in the suite accent it
   reads as a platform badge, which is what it is. */
.habit-product .product-title small{
  display:inline-flex;
  align-items:center;
  gap:6px;
  padding:4px 10px 4px 9px;
  border:1px solid rgba(var(--suite-accent),.42);
  border-radius:999px;
  background:rgba(var(--suite-accent),.1);
  color:rgb(var(--suite-accent));
  font-size:9px;
  letter-spacing:.14em;
}
.habit-product .product-title small::before{
  content:"";
  width:5px;
  height:5px;
  border-radius:50%;
  background:rgb(var(--suite-accent));
}

/* The pop. Starts collapsed and invisible, overshoots slightly, settles —
   and only once the room is actually open, so it is not spent while the
   visitor is still in the corridor. `both` holds the from-state before the
   delay elapses, otherwise the badge flashes at full size first. */
@keyframes hkBadgePop{
  0%  {opacity:0; transform:translateY(10px) scale(.72)}
  58% {opacity:1; transform:translateY(0)    scale(1.08)}
  78% {           transform:scale(.97)}
  100%{opacity:1; transform:translateY(0)    scale(1)}
}
/* No base opacity:0 here.
 *
 * It was `.habit-product .product-title small{opacity:0}` — which hides the
 * badge for good on any path that never applies .is-room-open. The animation
 * is declared `both`, so its backwards fill already holds the from-state
 * through the delay; the base rule bought nothing and risked a permanently
 * invisible badge if the script that sets that class ever failed to run. */
.habit-product.is-room-open .product-title small{
  animation:hkBadgePop .64s cubic-bezier(.22,1,.3,1) .22s both;
}
/* No motion, no animation — but the badge must still be VISIBLE, which a
   bare `animation:none` would not give it since the base state is opacity:0. */
@media (prefers-reduced-motion: reduce){
  .habit-product.is-room-open .product-title small{animation:none}
}

/* The meaning line, promoted. */
.product-room .room-note{
  margin:16px 0 0;
  padding-left:13px;
  border-left:2px solid rgba(var(--suite-accent),.5);
  font:400 13.5px/1.6 var(--font-interface, system-ui, sans-serif);
  color:rgba(240,243,241,.9);
}
.product-room .room-note i{
  font-style:normal;
  font-weight:600;
  color:rgb(var(--suite-accent));
}

@media (max-width:1050px){
  .habit-product .product-title{gap:18px}
  .habit-product .product-title img{width:80px;height:80px;border-radius:20px}

  .product-room .room-note{font-size:12.5px;padding-left:11px;margin-top:13px}
}

/* ── Finance Tracker room: show the screenshots whole ───────────────────────
 *
 * The strip here inherits .product-story>.shot-strip, which stretches each
 * thumb to the strip's height and then fills it with object-fit:cover. The
 * assets are 1:2; the thumbs were rendering 94x302. So every screen was
 * cropped to a tall sliver of its own top-left — six slivers in a row, none
 * of them legible as a screen.
 *
 * Now they keep their own ratio at 102px (204 tall), unframed like the Habit
 * room's: rounded corners on the artwork, a shadow to lift it, no border
 * box drawn over the picture. Six across fits the 682px column with room.
 *
 * Scoped to .finance-product so nothing else inheriting the story-strip
 * rules changes. */
.finance-product .product-story>.shot-strip{
  flex:0 0 auto;
  align-items:flex-start;
  gap:12px;
  min-height:0;
  /* 16, not 22: with the three story lines added the column ran 2px past its
     own height. Nothing was visibly clipped, but a container with
     overflow:hidden sitting 2px over is one line of copy away from eating a
     caption, which is exactly how the Habit strip lost its labels. */
  margin-top:10px;
}
/* The six share the column rather than claiming a fixed width.
 *
 * At a fixed 102px the row wanted 682px inside a 594px column and the sixth
 * thumb — "More" — was clipped by 88px, which is a thing you only see if you
 * look at the right-hand edge. flex:1 1 0 with min-width:0 lets them divide
 * whatever the column is, and because the img is width:100% with height:auto
 * the 1:2 ratio follows the width down. The row cannot overflow at any
 * column width, which is the property worth having here. */
.finance-product .product-story>.shot-strip .shot-thumb{
  flex:1 1 0;
  min-width:0;
  /* 132, not 112: the card is min(62vw,880px), so on a wide display the
     column reaches ~790px and six thumbs can be 121px each. Capping at 112
     would have left that width unused — and the whole reason this room is one
     column is the note above, "the screenshots get that width". */
  max-width:132px;
  display:flex;
  flex-direction:column;
  align-items:center;
  gap:9px;
  width:auto;
  height:auto;
  padding:0;
  border:0;
  background:none;
  overflow:visible;
  cursor:pointer;
  transition:transform .22s cubic-bezier(.23,1,.32,1);
}
.finance-product .product-story>.shot-strip .shot-thumb img{
  width:100%;
  height:auto;            /* was 100% + cover, which did the cropping */
  object-fit:fill;
  border-radius:13px;
  box-shadow:0 14px 32px rgba(0,0,0,.4);
}
.finance-product .product-story>.shot-strip .shot-thumb::after{
  content:attr(data-cap);
  font:600 9.5px/1.2 var(--font-interface, system-ui, sans-serif);
  letter-spacing:.08em;
  text-transform:uppercase;
  color:rgba(236,239,237,.6);
  text-shadow:0 1px 3px rgba(0,0,0,.75);
  white-space:nowrap;
}
.finance-product .product-story>.shot-strip .shot-thumb:hover,
.finance-product .product-story>.shot-strip .shot-thumb:focus-visible{
  transform:translateY(-6px);
}
.finance-product .product-story>.shot-strip .shot-thumb:hover::after,
.finance-product .product-story>.shot-strip .shot-thumb:focus-visible::after{
  color:rgb(var(--suite-accent));
}

/* The mark, matched to the Habit room so the two product rooms read as a
   pair rather than as two different templates. */
.finance-product .product-title img{
  width:80px;
  height:80px;
  flex:0 0 auto;
  border-radius:22px;
  box-shadow:0 20px 42px rgba(0,0,0,.42), 0 0 0 1px rgba(var(--suite-accent),.16);
}
.finance-product .product-title h2{
  overflow-wrap:normal;
  word-break:normal;
  hyphens:none;
}

@media (max-width:1050px){
  .finance-product .product-story>.shot-strip{
    gap:10px;
    overflow-x:auto;
    overscroll-behavior-x:contain;
    scroll-snap-type:x proximity;
    scrollbar-width:none;
    /* same 5px lift, same clipping strip — this one had no top
       padding at all, so the crop was the full 5px. */
    padding:6px 0 4px;
  }
  .finance-product .product-story>.shot-strip::-webkit-scrollbar{display:none}
  /* flex:0 0 96px, NOT width:96px.
     The desktop rule is flex:1 1 0 with min-width:0 so the six divide the
     column — correct there, fatal here: on a 375px screen they divided it
     into 43px slivers and the width declaration was simply ignored, because
     a flex-basis of 0 with shrink allowed beats it. Fixing the basis is what
     makes them hold their size and the row scroll instead. */
  .finance-product .product-story>.shot-strip .shot-thumb{
    flex:0 0 96px;
    max-width:none;
    scroll-snap-align:center;
  }
  .finance-product .product-story>.shot-strip .shot-thumb:hover{transform:none}
  .finance-product .product-title img{width:76px;height:76px;border-radius:19px}
}


/* ── The Finance room had no entrance at all ───────────────────────────────
 *
 * Measured, not assumed: on opening, this room ran exactly one animation —
 * roomBackgroundEnter, which belongs to the backdrop — while the Habit room
 * next door ran that plus its badge. Every rule in product-motion.css hangs
 * off .product-display, .display-window, .display-float, .icon-stage,
 * .learning-wall or .manager-label, and this room has none of them: its
 * display box was removed on purpose (see the note above about 60% empty
 * glass) and the motion layer went with it, because it had all been attached
 * to that box rather than to the room.
 *
 * So the entrance is given to what this room does have — the six screens,
 * arriving in order, which reads as the row being dealt out rather than six
 * things appearing at once.
 *
 * NO base opacity:0 anywhere. `both` carries the backwards fill through each
 * delay, so if .is-room-open is never applied the thumbs simply render
 * normally. A base rule would have made the whole strip permanently
 * invisible the day that class failed to land. */
@keyframes shotDeal{
  from{opacity:0; transform:translateY(18px) scale(.95)}
  to  {opacity:1; transform:none}
}
.product-room.is-room-open .shot-strip .shot-thumb{
  animation:shotDeal .5s cubic-bezier(.22,1,.3,1) both;
}
.product-room.is-room-open .shot-strip .shot-thumb:nth-child(1){animation-delay:.20s}
.product-room.is-room-open .shot-strip .shot-thumb:nth-child(2){animation-delay:.27s}
.product-room.is-room-open .shot-strip .shot-thumb:nth-child(3){animation-delay:.34s}
.product-room.is-room-open .shot-strip .shot-thumb:nth-child(4){animation-delay:.41s}
.product-room.is-room-open .shot-strip .shot-thumb:nth-child(5){animation-delay:.48s}
.product-room.is-room-open .shot-strip .shot-thumb:nth-child(6){animation-delay:.55s}

/* The UPI note arrives just after them — it is the line worth reading, and a
   beat of its own is what makes it read that way rather than as more copy. */
@keyframes noteIn{
  from{opacity:0; transform:translateX(-10px)}
  to  {opacity:1; transform:none}
}
.product-room.is-room-open .room-note{
  animation:noteIn .55s cubic-bezier(.22,1,.3,1) .62s both;
}

@media (prefers-reduced-motion: reduce){
  .product-room.is-room-open .shot-strip .shot-thumb,
  .product-room.is-room-open .room-note,
  .product-room.is-room-open .product-title img{animation:none}
}


/* ── The rooms must not go still ───────────────────────────────────────────
 *
 * Replacing the Habit room's mockups with real screenshots quietly took its
 * only continuous motion with them: .habit-board's five dots ran habitPulse
 * 2.8s infinite and .icon-stage's mark ran iconHover 4s infinite, so the room
 * was always faintly alive. The screenshots that replaced them are static
 * images, and the badge pop lasts 0.64s — after which the room simply stopped
 * moving. Removing a fake habit grid was right; removing the life with it was
 * not, and nothing in the markup showed that it had happened.
 *
 * The mark now carries that float itself, in both product rooms. Same idea as
 * iconHover, a touch slower and shallower because it sits beside the heading
 * rather than alone on a pedestal. */
@keyframes markFloat{50%{transform:translateY(-6px)}}
.product-room.is-room-open .product-title img{
  animation:markFloat 4.6s ease-in-out infinite;
}

/* ── The screens keep breathing after they arrive ──────────────────────────
 *
 * shotDeal is an entrance: it runs once and leaves the row static, which is
 * the same thing that happened when the pulsing habit-board was replaced.
 * This is the continuous half.
 *
 * It goes on the IMG, not on the .shot-thumb button, and that placement is
 * the whole trick. shotDeal and the :hover lift both animate transform on the
 * button; a second transform animation there would not compose with them —
 * whichever is declared last simply wins the property, so the entrance would
 * be eaten. The button slides and fades in, the image inside it drifts, and
 * the two never touch the same element.
 *
 * Negative delays, spread across the cycle, so the six are already out of
 * phase on the first frame. Started at zero they would rise and fall in
 * unison, which reads as the whole strip wobbling rather than six separate
 * screens settling. */
@keyframes shotFloat{50%{transform:translateY(-5px)}}
/* ONLY while the room is open, and no will-change.
 *
 * Written unconditionally this cost real frames: every screenshot in BOTH
 * rooms drifted forever, twelve compositor layers running for rooms the
 * visitor was not in. Measured at 4x CPU, project-office went from 0% dropped
 * scroll frames to about 15%, consistent across two runs. Scoped to the open
 * room it is six, only while they are on screen.
 *
 * will-change is gone too: a transform animation gets its own layer from the
 * compositor regardless, so all the hint bought here was holding twelve
 * layers resident. */
.product-room.is-room-open .shot-strip .shot-thumb img{
  animation:shotFloat 5.4s ease-in-out infinite;
}
.product-room.is-room-open .shot-strip .shot-thumb:nth-child(1) img{animation-delay:0s}
.product-room.is-room-open .shot-strip .shot-thumb:nth-child(2) img{animation-delay:-.9s}
.product-room.is-room-open .shot-strip .shot-thumb:nth-child(3) img{animation-delay:-1.8s}
.product-room.is-room-open .shot-strip .shot-thumb:nth-child(4) img{animation-delay:-2.7s}
.product-room.is-room-open .shot-strip .shot-thumb:nth-child(5) img{animation-delay:-3.6s}
.product-room.is-room-open .shot-strip .shot-thumb:nth-child(6) img{animation-delay:-4.5s}

@media (prefers-reduced-motion: reduce){
  .product-room.is-room-open .shot-strip .shot-thumb img{animation:none}
}
