Security Guide

MCP server CSS vertical-viewport-segments security — portrait horizontal fold seam consent targeting, env(fold-top/fold-bottom) positioning, top-panel-only consent isolation, vertical dual-screen layout omission, JS screen orientation and segments API

CSS @media (vertical-viewport-segments: 2) matches foldable devices with a horizontal fold — a clamshell-style phone opened flat, or a tablet-form foldable in portrait orientation with the hinge running across the middle of the screen. The two stacked panels are described by env(fold-top) and env(fold-bottom). Consent placed at the fold seam is in the device hinge. Consent locked to the top panel disappears from view when the user reads content in the bottom panel. Both attacks pass every standard single-screen audit.

CSS vertical-viewport-segments media feature — overview

@media (vertical-viewport-segments) is defined in the CSS Viewport Segments specification. It queries the number of vertical segments — that is, stacked panels created by a horizontal fold. A value of 2 means the viewport has two rows: a top panel and a bottom panel, with a horizontal seam between them. Environment variables: env(fold-top) — the top edge of the fold seam; env(fold-bottom) — the bottom edge; env(fold-height) — the seam height. This is distinct from horizontal-viewport-segments, which covers side-by-side panels from a vertical fold. Devices that match: clamshell foldables opened flat (e.g., Samsung Galaxy Z Flip in full-open mode), certain Surface Neo configurations. Most foldables default to horizontal-viewport-segments in their landscape orientation; vertical-viewport-segments occurs in portrait orientation with a horizontal crease. Standard single-panel devices always report 1. Related: horizontal-viewport-segments, orientation media query.

Attack 1: consent positioned at the horizontal fold seam

The fold seam runs horizontally across the display at env(fold-top) to env(fold-bottom). Pixels in this region are rendered by the display driver but correspond to the physical hinge. A consent button positioned in this horizontal band is visible in screenshots and bounding rect measurements, but the physical surface of the device at that location is the hinge crease — not the display panel. Users cannot tap a button at the fold seam.

/* Attack: consent positioned at the horizontal fold seam */
@media (vertical-viewport-segments: 2) {
  .consent-btn {
    position: fixed;
    top: calc(env(fold-top) + (env(fold-height) / 2) - 22px);
    left: 50%;
    transform: translateX(-50%);
    width: 44px;
    height: 44px;
    /* Button center is in the horizontal fold seam.
       On a clamshell foldable: the hinge runs across the width of the display here.
       The button is rendered and interactive (DOM, BCR, opacity all normal).
       The physical location is the hinge crease — user cannot touch it. */
  }
}
// Detection: CSSOM scan for env(fold-top/fold-bottom) on consent elements
function auditVerticalFoldSeamPlacement(consentEl) {
  for (const sheet of document.styleSheets) {
    try {
      for (const rule of sheet.cssRules) {
        if (rule.type !== CSSRule.MEDIA_RULE) continue;
        const mq = rule.conditionText || rule.media.mediaText;
        if (!/vertical-viewport-segments/.test(mq)) continue;
        for (const inner of rule.cssRules) {
          if (inner.type !== CSSRule.STYLE_RULE) continue;
          try { if (!consentEl.matches(inner.selectorText)) continue; }
          catch (e) { continue; }
          const cssText = inner.cssText;
          if (/env\s*\(\s*fold-(top|bottom|height)/.test(cssText)) {
            console.warn('[SkillAudit] vertical-viewport-segments media rule positions consent element',
              'using env(fold-top/fold-bottom/fold-height);',
              'on horizontal-fold devices the seam is a physical hinge — consent may be untappable;',
              'media:', mq, 'selector:', inner.selectorText);
          }
          // Also check for hide rules
          const s = inner.style;
          if (s.display === 'none' || s.opacity === '0' || s.visibility === 'hidden') {
            console.warn('[SkillAudit] vertical-viewport-segments media rule hides consent element;',
              'affects clamshell foldables in open mode;',
              'media:', mq, 'selector:', inner.selectorText);
          }
        }
      }
    } catch (e) {}
  }
}

Attack 2: consent locked to the top panel — unreachable from bottom-panel content

In vertical dual-screen mode, the page content may span both panels. An MCP server renders the consent disclosure only in the top panel — it is visible when the user first opens the page and the top panel is prominent. As the user scrolls to bottom-panel content, the fixed-position top-panel consent moves out of view, or the top-panel area is restructured so that the consent is not visible in the bottom panel's context. The user engages with the content in the bottom panel without having been presented with the consent disclosure.

/* Attack: consent locked to top panel using max-height */
@media (vertical-viewport-segments: 2) {
  body {
    display: grid;
    grid-template-rows: env(fold-top) 1fr; /* top panel | bottom panel */
    height: 100vh;
    overflow: hidden;
  }

  .top-panel {
    height: env(fold-top);
    overflow: hidden;
    /* consent-banner rendered here, inside the top panel grid area */
  }

  .bottom-panel {
    /* No consent present in bottom panel.
       User sees content in the bottom panel without consent exposure. */
  }

  /* consent-banner is a child of .top-panel but .top-panel
     is not in the user's active viewport once they scroll to bottom. */
  .consent-banner {
    /* Stays in .top-panel — inaccessible once bottom panel is active */
  }
}
// Detection: consent element restricted to top panel
function auditTopPanelConsentIsolation(consentEl) {
  const isMultiSegment = window.matchMedia('(vertical-viewport-segments: 2)').matches;
  if (isMultiSegment) {
    const bcr = consentEl.getBoundingClientRect();
    const foldTopVal = parseInt(
      getComputedStyle(document.documentElement).getPropertyValue('env(fold-top)') || '0'
    );
    if (foldTopVal > 0 && bcr.bottom <= foldTopVal) {
      console.warn('[SkillAudit] consent element is entirely within the top panel (BCR bottom:',
        bcr.bottom, '<= fold-top:', foldTopVal, ');',
        'users engaging with bottom panel content may not see consent;', consentEl);
    }
  }
}

Top-panel consent is not the same as no consent: The consent element exists. It's in the DOM. It may have been visible when the page loaded. But in a vertical dual-screen layout where the bottom panel is a self-contained content area, users who scroll into the bottom panel context have effectively left the consent disclosure behind. This is functionally similar to a scroll-anchor attack — consent present but not encountered by the user.

Attack 3: vertical dual-screen layout omits consent disclosure

As with horizontal-viewport-segments, a direct structural attack replaces the entire page layout under @media (vertical-viewport-segments: 2) with a two-panel template that doesn't include the consent element. The restructured template renders content in both panels but neither panel includes a consent disclosure.

/* Attack: full page restructure omits consent */
.consent-banner { display: block; }

@media (vertical-viewport-segments: 2) {
  /* Two-panel vertical layout */
  body {
    display: grid;
    grid-template-rows: env(fold-top) 1fr;
    height: 100vh;
    overflow: hidden;
  }
  /* content sections assigned to grid areas — consent-banner not assigned */
  .consent-banner {
    display: none; /* or: moved to a DOM area that is overflow:hidden */
  }
  .app-content { grid-row: 1 / 2; }
  .app-detail  { grid-row: 2 / 3; }
}

Attack 4: JS screen.orientation + segments API consent removal

JavaScript can combine screen.orientation.type to detect portrait orientation with the Window Segments API to confirm vertical dual-screen mode, and conditionally remove the consent element. The combination provides precise targeting: only users on a clamshell foldable opened flat in portrait mode.

// Attack: portrait orientation + vertical segments detection
async function applyFoldableMode() {
  const isPortrait = screen.orientation?.type?.includes('portrait') ?? false;
  const segments = navigator.windowSegments
    ?? window.getWindowSegments?.()
    ?? null;
  const isVerticalFold = isPortrait && Array.isArray(segments) && segments.length >= 2;
  const isCSSVerticalFold = window.matchMedia('(vertical-viewport-segments: 2)').matches;

  if (isVerticalFold || isCSSVerticalFold) {
    document.querySelector('.consent-banner')?.remove();
  }
}

applyFoldableMode();
screen.orientation?.addEventListener('change', applyFoldableMode);
// Detection: JS source scan for vertical-viewport-segments + consent removal
function auditVerticalSegmentsJS() {
  for (const script of document.querySelectorAll('script')) {
    const src = script.textContent;
    if (!src) continue;
    const hasSegments = /vertical-viewport-segments|windowSegments|getWindowSegments/.test(src);
    if (!hasSegments) continue;
    if (!/consent|banner|modal|btn|permission/i.test(src)) continue;
    const hasRemove = /\.remove\(\)|display.*none|replaceWith|replaceChild/.test(src);
    if (hasRemove) {
      console.warn('[SkillAudit] script uses vertical-viewport-segments or Window Segments API',
        'with consent DOM removal;',
        'current vertical-viewport-segments:', window.matchMedia('(vertical-viewport-segments: 2)').matches,
        'script:', script.src || '(inline)');
    }
  }
}

SkillAudit audits all CSS Viewport Segments media rules — both horizontal-viewport-segments and vertical-viewport-segments — and flags env(fold-*) position values on consent elements, without requiring a foldable device. Run a free audit on your MCP server.

Findings summary

High vertical-viewport-segments:2 rule positions consent element using env(fold-top/fold-bottom/fold-height) — on horizontal-fold devices the positioned element falls in the physical hinge area and cannot be tapped; detected by CSSOM scan for env(fold-*) vertical position values on consent elements in vertical-viewport-segments media blocks.
Medium consent element isolated to top panel in vertical dual-screen layout — users engaging with bottom panel content never see the consent disclosure; detected by BCR check of consent element relative to env(fold-top) boundary on foldable devices, and CSSOM scan for top-panel-only grid placement.
High vertical-viewport-segments:2 layout restructure sets consent to display:none or removes it from the template — consent absent for clamshell foldable users in open-flat portrait mode; detected by CSSOM scan for display:none on consent elements in vertical-viewport-segments media rules.
Medium JS screen.orientation + Window Segments API consent removal — portrait fold mode detection used to remove consent for clamshell-foldable users; detected by source scan for window segments API combined with consent removal patterns.