/* ==========================================================================
   The shared dialog used for both case files and capability modules.
   ========================================================================== */

/* ---------- the shared dialog: case files and capability modules ----------
   Structure is the whole point of this component. The right-hand panel is a
   three-row grid — pinned head, scrolling body, pinned foot — because the
   earlier version let the entire dialog scroll: the close button left the
   viewport, the title clipped against the top edge, and the photograph
   scrolled away with them.

   min-height:0 on the panel and the body is load-bearing. A grid child
   defaults to min-height:auto and refuses to shrink below its content, so
   without it the body never becomes a scroll container and the whole dialog
   grows past the viewport again. */
.cs-modal{ position:fixed; inset:0; z-index:120; display:flex; align-items:center; justify-content:center; padding:max(4vw,20px); cursor:default; }
/* Two locks, deliberately. [hidden] is the semantic one modal.js sets, and
   :not(.open) is the invariant: this element is position:fixed across the
   whole viewport, so if it is ever displayed while closed it silently eats
   every click on the page behind it. The CSS must not depend on the JS
   remembering. */
.cs-modal[hidden], .cs-modal:not(.open){ display:none; }
.cs-modal button, .cs-modal a{ cursor:pointer; }

.cs-backdrop{
  position:absolute; inset:0; background:rgba(var(--backdrop),.86);
  opacity:0; backdrop-filter:blur(0px); -webkit-backdrop-filter:blur(0px);
  transition:opacity .5s var(--ease-out), backdrop-filter .5s var(--ease-out), -webkit-backdrop-filter .5s var(--ease-out);
}
.cs-modal.in .cs-backdrop{ opacity:1; backdrop-filter:blur(10px); -webkit-backdrop-filter:blur(10px); }

.cs-dialog{
  position:relative; z-index:1;
  width:min(1120px,94vw); max-height:min(88vh,820px);
  background:var(--elev); border:1px solid var(--line);
  /* Elevation. Every other panel on the page carries this shadow; without it
     the dialog reads as a flat cut-out rather than something lifted above the
     blurred page. */
  box-shadow:0 44px 110px -34px rgba(0,0,0,.8);
  display:grid; grid-template-columns:1fr 1.06fr;
  /* The single row must be minmax(0,1fr), not the implicit auto. An auto row
     sizes to its tallest content, so max-height merely CLIPPED the overflow —
     the panel stayed 906px inside a 792px box and the footer was cut off
     below the fold with no way to reach it. Constraining the row is what
     actually hands the leftover space to .cs-body to scroll in. */
  grid-template-rows:auto minmax(0,1fr);
  overflow:hidden;                       /* the BODY scrolls, never this */
  opacity:0; transform:translateY(20px) scale(.982);
  transition:opacity .5s var(--ease-out), transform .5s var(--ease-out);
}
.cs-modal.in .cs-dialog{ opacity:1; transform:none; }

/* ---- title bar ---- */
.cs-bar{
  grid-column:1 / -1;
  /* min-width:0 is the whole reason the close button was reachable on a
     desktop and 375px off-screen on a phone. A grid item defaults to
     min-width:auto, so the bar refused to shrink below the natural width of
     the module's tag list — pushing Close outside the dialog, where
     overflow:hidden clipped it. Same trap as min-height:0 on .cs-panel.
     .cs-client already had min-width:0; the parent needed it too. */
  min-width:0;
  display:flex; align-items:center; gap:clamp(12px,1.6vw,22px);
  padding:0 0 0 clamp(18px,2vw,26px);
  border-bottom:1px solid var(--line); background:var(--panel);
}
.cs-bar .cs-idx{
  flex:0 0 auto; font-size:var(--fs-2xs); letter-spacing:.2em; text-transform:uppercase;
  color:var(--amber); border:1px solid currentColor; padding:3px 9px;
}
.cs-bar .cs-client{
  flex:1 1 auto; min-width:0;
  font-size:var(--fs-2xs); letter-spacing:.16em; text-transform:uppercase; color:var(--faint);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;   /* long tag lists truncate rather than wrap the bar */
}

/* ---- image column: fills the dialog and stays put ---- */
.cs-img{ position:relative; overflow:hidden; min-height:0; background:var(--media-bg); }
.cs-img img{
  width:100%; height:100%; object-fit:cover;
  filter:grayscale(.3) brightness(.72) contrast(1.05);
  transform:scale(1.06); transition:transform 1.4s var(--ease-out);
}
.cs-modal.in .cs-img img{ transform:scale(1); }   /* settles as the panel deals in */
[data-theme="light"] .cs-img img{ filter:grayscale(.15) brightness(.95); }
.cs-img .duo{ position:absolute; inset:0; background:linear-gradient(115deg, rgba(var(--duo-rgb),.2), rgba(var(--duo-rgb),.85)); }

/* ---- panel ---- */
.cs-panel{ display:grid; grid-template-rows:auto minmax(0,1fr) auto; min-height:0; min-width:0; }
.cs-head{ padding:clamp(24px,2.8vw,38px) clamp(26px,3.2vw,46px) clamp(16px,1.8vw,22px); border-bottom:1px solid var(--line); }
.cs-foot{ padding:clamp(18px,2vw,26px) clamp(26px,3.2vw,48px) clamp(24px,2.6vw,34px); border-top:1px solid var(--line); }

.cs-body{ position:relative; min-height:0; }
.cs-scroll{
  height:100%; overflow-y:auto; overscroll-behavior:contain;
  padding:clamp(20px,2.4vw,30px) clamp(26px,3.2vw,48px);
  /* A hairline scrollbar in theme colours. The default chrome on a dark
     panel is the single loudest thing in the component. */
  scrollbar-width:thin; scrollbar-color:var(--field-line) transparent;
}
.cs-scroll::-webkit-scrollbar{ width:6px; }
.cs-scroll::-webkit-scrollbar-track{ background:transparent; }
.cs-scroll::-webkit-scrollbar-thumb{ background:var(--field-line); border-radius:0; }
.cs-scroll::-webkit-scrollbar-thumb:hover{ background:var(--muted); }

/* Edge fades, each shown only when there is more content that way. On the
   wrapper rather than the scroller, or they would scroll along with it. */
.cs-body::before,.cs-body::after{
  content:""; position:absolute; left:0; right:6px; height:38px; z-index:2;
  pointer-events:none; opacity:0; transition:opacity .3s var(--ease-out);
}
.cs-body::before{ top:0; background:linear-gradient(var(--elev), transparent); }
.cs-body::after{ bottom:0; background:linear-gradient(transparent, var(--elev)); }
.cs-body.more-up::before{ opacity:1; }
.cs-body.more-down::after{ opacity:1; }

/* Read position, on the inner edge. Only drawn when there is something to
   report. */
.cs-prog{
  /* Left edge, not right: at right:0 it sat in the same six pixels as the
     scrollbar thumb — two readouts of one thing, stacked. On the inner edge
     it borders the image column and reads as the panel's own margin. */
  position:absolute; left:0; top:0; width:2px; height:100%; z-index:3;
  transform:scaleY(0); transform-origin:top; background:var(--accent);
  opacity:0; transition:opacity .3s var(--ease-out);
}
.cs-body.scrolls .cs-prog{ opacity:.55; }

/* ---- close ---- */
/* In the bar rather than floating over content. Square, flush to the top
   right corner, divided from the meta by its own left border. */
.cs-close{
  flex:0 0 auto; width:52px; align-self:stretch; min-height:46px;
  display:flex; align-items:center; justify-content:center;
  color:var(--muted); background:none; border:0; border-left:1px solid var(--line);
  transition:background .3s var(--ease-out), color .3s var(--ease-out);
}
.cs-close svg{ width:18px; height:18px; display:block; }
/* Neutral, not --alert. Closing a dialog destroys nothing, and a red
   control implies it does. */
@media (hover:hover){ .cs-close:hover{ background:var(--hover); color:var(--ink); } }
.cs-close:focus-visible{ background:var(--hover); color:var(--ink); outline-offset:-3px; }
/* The dialog takes focus on open so the label is announced; it must not
   then draw a ring around the entire panel. */
.cs-dialog:focus{ outline:none; }

/* ---- staged entry ----
   Head, body and foot children share one index sequence, assigned in reading
   order by modal.js, so the panel deals itself in. The exit is not the
   reverse: .in comes off and everything leaves together, because replaying a
   stagger backwards reads as hesitation. */
.cs-dialog [data-stag]{
  opacity:0; transform:translateY(9px);
  transition:opacity .45s var(--ease-out), transform .45s var(--ease-out);
}
.cs-modal.in .cs-dialog [data-stag]{
  opacity:1; transform:none;
  transition-delay:calc(var(--i,0) * 55ms + 110ms);
}

/* ---- content ---- */
.cs-idx{ font-size:var(--fs-sm); letter-spacing:.2em; color:var(--amber); text-transform:uppercase; }
.cs-client{ margin-top:10px; font-size:var(--fs-sm); letter-spacing:.16em; text-transform:uppercase; color:var(--muted); }
.cs-title{ margin-top:14px; font-family:var(--display); font-weight:700; text-transform:uppercase; font-size:clamp(1.3rem,2.1vw,1.9rem); line-height:1.04; letter-spacing:-.02em; }
.cs-desc{ font-size:var(--fs-base); line-height:1.85; color:var(--muted); }
.cs-scroll .p-results{ display:flex; flex-wrap:wrap; gap:22px 40px; margin-top:30px; }
.cs-scroll .p-results .rv{ display:block; font-family:var(--display); font-weight:800; font-size:clamp(1.45rem,2.3vw,1.95rem); line-height:1.04; color:var(--accent); }
.cs-scroll .p-results .rl{ display:block; margin-top:8px; font-size:var(--fs-2xs); letter-spacing:.14em; text-transform:uppercase; color:var(--muted); max-width:16ch; }
.cs-scroll .p-quote{ margin-top:30px; font-family:var(--display); font-weight:500; font-size:clamp(1rem,1.8vw,1.35rem); line-height:1.4; color:var(--ink); }
.cs-scroll .p-quote cite{ display:block; margin-top:14px; font-family:var(--mono); font-style:normal; font-size:var(--fs-2xs); letter-spacing:.16em; text-transform:uppercase; color:var(--muted); }
.cs-scroll .cap-incs-h{ margin-top:32px; font-size:var(--fs-xs); letter-spacing:.2em; text-transform:uppercase; color:var(--amber); }
.cs-scroll .cap-incs{ list-style:none; margin-top:16px; display:grid; grid-template-columns:1fr 1fr; gap:13px 26px; }
.cs-scroll .cap-incs li{ position:relative; padding-left:22px; font-size:var(--fs-md); line-height:1.55; color:var(--muted); }
.cs-scroll .cap-incs li::before{ content:"\2197"; position:absolute; left:0; top:1px; color:var(--accent); font-size:var(--fs-sm); }
.cs-scroll .cap-ideal{ margin-top:30px; padding-top:22px; border-top:1px solid var(--line); font-size:var(--fs-base); line-height:1.65; color:var(--ink); }
.cs-scroll .cap-ideal span{ display:block; margin-bottom:7px; font-size:var(--fs-xs); letter-spacing:.16em; text-transform:uppercase; color:var(--muted); }
@media (max-width:1100px){ .cs-scroll .cap-incs{ grid-template-columns:1fr; } }

.cs-cta{
  display:inline-flex; align-items:center; gap:10px; min-height:52px; padding:0 26px;
  background:var(--accent); color:var(--on-accent);
  font-size:var(--fs-sm); letter-spacing:.16em; text-transform:uppercase;
  transition:transform .4s var(--ease-out), filter .4s var(--ease-out);
}
@media (hover:hover){ .cs-cta:hover{ transform:translateY(-2px); filter:brightness(1.1); } }

/* ---- narrow ----
   The image becomes a banner and the panel takes the rest. The dialog keeps
   its cap, so the body still scrolls rather than the page behind it. */
/* ---- narrow: one column, image as a banner ----
   The banner was 26vh, which took 220px of a 760px dialog and left the body
   reading through a 320px slot. It is atmosphere; the copy is the point. */
@media (max-width:900px){
  .cs-modal{ padding:max(3vw,14px); }
  .cs-dialog{
    grid-template-columns:1fr; grid-template-rows:auto auto minmax(0,1fr);
    width:min(640px,96vw); max-height:min(92vh,780px);
  }
  .cs-img{ min-height:0; height:clamp(112px,19vh,168px); }
  .cs-modal.in .cs-img img{ transform:scale(1); }
  .cs-head{ padding:clamp(18px,3.4vw,26px) clamp(18px,4vw,26px) clamp(12px,2vw,16px); }
  .cs-scroll{ padding:clamp(16px,3vw,22px) clamp(18px,4vw,26px); }
  .cs-foot{ padding:clamp(14px,2.4vw,18px) clamp(18px,4vw,26px) clamp(18px,3vw,22px); }
  .cs-cta{ width:100%; justify-content:center; }
}

/* ---- short: the photograph goes ----
   A landscape phone is ~390px tall. Two columns there left the body 101px
   deep with 957px hidden inside it, which is not a readable panel by any
   measure. Height is the discriminator, not width: 1024x768 is fine in two
   columns and 844x390 is not, so this is keyed on height and overrides the
   width rule above. */
@media (max-height:600px){
  .cs-dialog{
    grid-template-columns:1fr; grid-template-rows:auto minmax(0,1fr);
    width:min(680px,96vw); max-height:94vh;
  }
  .cs-img{ display:none; }
  .cs-head{ padding:12px clamp(18px,3vw,26px) 10px; }
  .cs-title{ font-size:clamp(1.02rem,2.6vw,1.3rem); }
  .cs-bar{ padding-left:clamp(14px,2vw,20px); }
  .cs-scroll{ padding:12px clamp(18px,3vw,26px); }
  .cs-foot{ padding:10px clamp(18px,3vw,26px) 12px; }
  .cs-cta{ min-height:46px; }
}

@media (prefers-reduced-motion:reduce){
  .cs-backdrop,.cs-dialog,.cs-img img,.cs-close,.cs-cta,.cs-prog,
  .cs-body::before,.cs-body::after,
  .cs-dialog [data-stag]{ transition:none !important; }
  .cs-dialog [data-stag]{ transition-delay:0s !important; }
  .cs-img img{ transform:none; }
}

/* ==========================================================================
   The enquiry confirmation dialog (success-modal.js).
   Narrower and quieter than .cs-modal above: a receipt, not a content panel.
   ========================================================================== */

.ok-modal{ position:fixed; inset:0; z-index:130; display:flex; align-items:center; justify-content:center; padding:max(4vw,20px); }
.ok-modal[hidden]{ display:none; }
.ok-modal button, .ok-modal a{ cursor:pointer; }
.ok-backdrop{ position:absolute; inset:0; background:rgba(var(--backdrop),.82); backdrop-filter:blur(9px); -webkit-backdrop-filter:blur(9px); opacity:0; transition:opacity .42s var(--ease-out); }
.ok-modal.open .ok-backdrop{ opacity:1; }

.ok-dialog{
  position:relative; z-index:1; width:min(560px,94vw); max-height:88vh; overflow:auto;
  background:var(--elev); border:1px solid var(--line);
  padding:clamp(30px,4vw,48px);
  opacity:0; transform:translateY(24px) scale(.97);
  transition:opacity .42s var(--ease-out), transform .42s var(--ease-out);
}
.ok-modal.open .ok-dialog{ opacity:1; transform:none; }

.ok-close{ position:absolute; top:10px; right:12px; width:42px; height:42px; display:flex; align-items:center; justify-content:center; font-size:26px; line-height:1; color:var(--muted); background:none; border:1px solid transparent; transition:color .35s var(--ease-out), border-color .35s var(--ease-out); }
.ok-close:hover{ color:var(--ink); border-color:var(--line); }

/* The tick draws itself once. It is the only motion in the dialog, and it is
   what makes the panel read as a confirmation before a word is processed. */
.ok-mark{ width:52px; height:52px; color:var(--accent); }
.ok-mark svg{ width:100%; height:100%; display:block; }
.ok-ring{ stroke-dasharray:145; stroke-dashoffset:145; animation:ok-draw .62s var(--ease-out) .06s forwards; }
.ok-tick{ stroke-dasharray:34; stroke-dashoffset:34; animation:ok-draw .38s var(--ease-out) .5s forwards; }
@keyframes ok-draw{ to{ stroke-dashoffset:0; } }

.ok-sys{ margin-top:22px; font-size:var(--fs-2xs); letter-spacing:.22em; text-transform:uppercase; color:var(--muted); }
.ok-sys b{ color:var(--accent); font-weight:500; }
.ok-title{ margin-top:12px; font-family:var(--display); font-weight:700; text-transform:uppercase; font-size:clamp(1.5rem,3.4vw,2.2rem); line-height:1.02; letter-spacing:-.02em; }
.ok-desc{ margin-top:16px; font-size:var(--fs-base); line-height:1.8; color:var(--muted); }

/* Same counter treatment as .next-list in contact.css, so the two lists read
   as the same component in two places rather than as two components. */
.ok-steps{ list-style:none; counter-reset:okstep; margin-top:26px; padding-top:24px; border-top:1px solid var(--line); display:grid; gap:12px; }
.ok-steps li{ counter-increment:okstep; position:relative; padding-left:30px; font-size:var(--fs-md); line-height:1.6; color:var(--muted); }
.ok-steps li::before{ content:counter(okstep,decimal-leading-zero); position:absolute; left:0; top:.1em; font-size:var(--fs-2xs); letter-spacing:.1em; color:var(--accent); }

.ok-urgent{ margin-top:26px; padding-top:22px; border-top:1px solid var(--line); }
.ok-urgent-k{ font-size:var(--fs-2xs); letter-spacing:.2em; text-transform:uppercase; color:var(--amber); }
.ok-lines{ margin-top:10px; display:flex; flex-wrap:wrap; gap:6px 26px; }
.ok-lines a{ display:inline-flex; align-items:center; min-height:44px; font-size:var(--fs-md); color:var(--ink); border-bottom:1px solid transparent; transition:color .35s var(--ease-out), border-color .35s var(--ease-out); }
.ok-lines a:empty{ display:none; }
@media (hover:hover){ .ok-lines a:hover{ color:var(--accent); border-bottom-color:var(--accent); } }

.ok-done{
  margin-top:30px; width:100%; min-height:52px; padding:0 28px;
  display:inline-flex; align-items:center; justify-content:center;
  background:var(--accent); color:var(--on-accent); border:1px solid var(--accent);
  font-size:var(--fs-sm); letter-spacing:.18em; text-transform:uppercase;
  transition:filter .4s var(--ease-out);
}
@media (hover:hover){ .ok-done:hover{ filter:brightness(1.08); } }

@media (prefers-reduced-motion:reduce){
  .ok-backdrop,.ok-dialog{ transition:none; }
  .ok-ring,.ok-tick{ animation:none; stroke-dashoffset:0; }
}
