/* ============================================================
   EYE Website 3 — natural-scroll variant
   Same composition as style.css, but nothing is pinned: logo,
   title, connector, navigation, news and footer all sit in the
   document and scroll away with the copy.

   What that changes
   -----------------
   1. The red field can no longer be one 3858px shape. Past the
      point where the letter's back goes vertical it continues as
      .e-field, a plain block that runs to the foot of the page.
   2. The reading column is bounded by that vertical edge rather
      than by wherever the curve happens to be at the bottom of the
      viewport, so it settles at 710 rather than 750 at 1440.
   3. The news list follows the nav at a fixed distance instead of
      hanging off the bottom of the window.

   --vw
   ----
   A scrolling page has a scrollbar, and 100vw includes it. Every
   fluid value below would then be computed for a viewport 15px
   wider than the one being laid out. connector.js publishes the
   real width as --vw; 100vw is the fallback until it runs.

   Fluid range 1440 -> 1024, as in style.css. Below 1024 the desktop
   composition is abandoned rather than squeezed — see the "Compact
   layout" section at the foot of this file.

   Breakpoints
   -----------
   One structural breakpoint, at 1024px, and fluid clamps either side
   of it. 1024 is not a device size picked off a list: it is the width
   at which the reading column (605) + gutter (72) and the news column
   (215) + its margin (45) stop both fitting, which is the same width
   at which the red field stops having anything to hold. Below it the
   field and the sidebar are withdrawn together and the sidebar comes
   back as a full-screen overlay behind the menu button.

   Inside the compact range every metric interpolates 360 -> 1024 in
   the same clamp form as above, so there is nothing to re-tune at 768
   or 480; the one exception is prefers-reduced-motion.

   Media queries are in px, not em, deliberately: the whole grid is
   drawn in px and the type does not scale with the browser's font
   size, so an em query would move the breakpoint away from the layout
   maths it is describing. They also measure the real viewport rather
   than --vw, so on a desktop browser with classic scrollbars the
   switch happens within a scrollbar's width of where --vw says it
   should — immaterial, and moot on the touch screens it is for.
   ============================================================ */

/* ---- fonts ---------------------------------------------------
   Self-hosted, Latin subsets. Roboto Flex is the variable font: one file
   for every weight (400 footer, 500 subnav, 800 title and nav) and the
   optical size the browser picks from the font size. IBM Plex comes as
   static instances, the styles the page uses and no others. */
@font-face {
  font-family: "Roboto Flex";
  font-style: normal;
  font-weight: 100 1000;
  font-display: swap;
  src: url("../Fonts/roboto-flex-v30-latin-variable.woff2") format("woff2");
}

@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../Fonts/ibm-plex-sans-v23-latin-regular.woff2") format("woff2");
}

@font-face {
  font-family: "IBM Plex Sans";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("../Fonts/ibm-plex-sans-v23-latin-italic.woff2") format("woff2");
}

@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../Fonts/ibm-plex-sans-v23-latin-700.woff2") format("woff2");
}

@font-face {
  font-family: "IBM Plex Sans";
  font-style: italic;
  font-weight: 700;
  font-display: swap;
  src: url("../Fonts/ibm-plex-sans-v23-latin-700italic.woff2") format("woff2");
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../Fonts/ibm-plex-mono-v20-latin-regular.woff2") format("woff2");
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../Fonts/ibm-plex-mono-v20-latin-500.woff2") format("woff2");
}

:root {
  --viewport: var(--vw, 100vw);
  --grid-width: min(1440px, var(--viewport));

  --eye-red: #d20019;
  --ink: #000;
  --paper: #fff;

  --font-display: "Roboto Flex", system-ui, sans-serif;
  --font-body: "IBM Plex Sans", system-ui, sans-serif;
  /* Technical accents — the news list, and whatever else reads as data
     rather than as copy. */
  --font-mono: "IBM Plex Mono", ui-monospace, monospace;

  /* ---- fluid horizontal metrics ------------------------------ */
  --gutter: clamp(72px, calc(72px + (var(--viewport) - 1024px) * 0.0264423), 83px);
  --nav-right: clamp(45px, calc(45px + (var(--viewport) - 1024px) * 0.1009615), 87px);
  --connector-x: clamp(30px, calc(30px + (var(--viewport) - 1024px) * 0.0480769), 50px);
  --news-width: clamp(215px, calc(215px + (var(--viewport) - 1024px) * 0.4312500), 350px);
  --foot-width: clamp(274px, calc(274px + (var(--viewport) - 1024px) * 0.6354167), 360px);

  /* Bounded by --e-left, the vertical back of the letter, since on a long page
     the column runs past the curve entirely: 710 -> 605. */
  --content-width: clamp(605px, calc(605px + (var(--viewport) - 1024px) * 0.2524038), 710px);

  /* ---- fluid type -------------------------------------------- */
  /* One scale, keyed to the body at 18: the title and the nav at twice
     that, the lead between the two, the news and the side column one step
     under the copy. IBM Plex sets a hair smaller than its size suggests
     (cap height 0.698em), so the steps are kept close. */
  --title-size: clamp(28px, calc(28px + (var(--viewport) - 1024px) * 0.0192308), 36px);
  --nav-size: clamp(28px, calc(28px + (var(--viewport) - 1024px) * 0.0192308), 36px);
  --body-size: clamp(16px, calc(16px + (var(--viewport) - 1024px) * 0.0048077), 18px);
  --lead-size: 28px;
  --news-size: clamp(15px, calc(15px + (var(--viewport) - 1024px) * 0.0024038), 16px);
  --foot-size: clamp(13px, calc(13px + (var(--viewport) - 1024px) * 0.0024038), 14px);

  --line: 1.334;
  --title-line: calc(var(--title-size) * var(--line));
  --nav-line: calc(var(--nav-size) * var(--line));
  --news-line: calc(var(--news-size) * var(--line));
  --news-gap: var(--news-line);

  /* ---- the "E" ----------------------------------------------- */
  --e-left: clamp(693px, calc(693px + (var(--viewport) - 1024px) * 0.2812500), 810px);
  --e-width: clamp(1336px, calc(1336px + (var(--viewport) - 1024px) * 4.4903846), 3204px);
  /* Where the letter's back has gone vertical and .e-field takes over.
     SVG y 1852..2006 is the straight section; 1860 - 526 = 1334 on the page. */
  --e-handover: 1334px;

  /* ---- vertical metrics (from the Figma frame) --------------- */
  /* How far below the top of .page the page title actually ends — published by
     connector.js, because a title that wraps is a rendered height and CSS
     cannot read one. 0 until it runs (and if it never does), which leaves both
     --rule-top values below at exactly the numbers the frame draws. */
  --title-bottom: 0px;
  --logo-top: 69px;
  --title-top: 232px;
  /* 285 in the frame, where the title is one line. max() so a title that wraps
     pushes the rule down instead of being struck through by it: for one line
     the measured edge is always above 285, so nothing moves. */
  --rule-top: max(285px, calc(var(--title-bottom) + 5px));
  --nav-top: 314px;
  /* How far below the middle of a nav item's line box the connector meets it:
     at display weight the optical centre sits a little under the box centre.
     On :root rather than on .connector because the overlay's own wire to the
     active item has to land on exactly the same line. */
  --nav-nudge: 3px;
  --sidebar-gap: 138px;       /* nav bottom to news top, as in the frame */
  --foot-top: 80px;
  --foot-bottom: 26px;
  --bullet: 14px;

  /* Only the compact layout has a menu button, but the two metrics are declared
     here so .navtoggle's rules resolve outside the media query; the compact
     block gives --fab-inset its real, fluid value. */
  --fab-size: 56px;
  --fab-inset: 24px;

  --title-mid: calc(var(--title-top) + var(--title-line) / 2 + 6px);
  --rule-split: clamp(839px, calc(839px + (var(--viewport) - 1024px) * 0.7716346), 1160px);
  --bleed: max(0px, (var(--viewport) - var(--grid-width)) / 2);
}

* { box-sizing: border-box; }

/* A jump to an anchor lands with its target a line below the top of the
   window rather than on the very edge; on a phone, a line below the pinned
   masthead (see the compact block). Scrolling glides where the browser can,
   and only for readers who have not asked for less motion; elsewhere it
   jumps, as it always did. */
html {
  scrollbar-gutter: stable;
  scroll-padding-top: calc(var(--body-size) * var(--line));
}

@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

body {
  position: relative;         /* containing block for .backdrop */
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--body-size);
  line-height: var(--line);
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; }


/* ------------------------------------------------------------
   Backdrop — spans the document, scrolls with it
   ------------------------------------------------------------ */
.backdrop {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;           /* the shapes bleed past the grid, not the page */
}

.backdrop__grid {
  position: relative;
  width: var(--grid-width);
  height: 100%;
  margin-inline: auto;
}

.e-shape {
  position: absolute;
  max-width: none;
}

/* The shoulder. Clipped just past the point where the back goes vertical —
   left whole, the letter's bottom curve would swing back to the right and
   pinch the field shut halfway down a long page. */
.e-shape--right {
  top: -526px;
  height: 3858px;
  width: var(--e-width);
  left: var(--e-left);
  clip-path: inset(0 0 1998px 0);   /* keep SVG y 0..1860 */
}

/* Carries the red from the handover down to the foot of the page. Its left
   edge is the letter's vertical back, so the join is seamless. */
.e-field {
  position: absolute;
  top: calc(var(--e-handover) - 4px);   /* 4px overlap, no hairline seam */
  bottom: 0;
  left: var(--e-left);
  right: calc(-1 * var(--bleed));
  background: var(--eye-red);
}

/* Switched off. It read as a stray red wedge under the last lines of the
   reading column rather than as part of the letter. Kept intact rather than
   deleted so it can be brought back by dropping the display line.

   The corner sliver sits at the foot of the document rather than the window —
   2308px is how far the shape's bottom falls below the 1024 frame. */
.e-shape--left {
  display: none;
  width: 3204px;
  height: 3858px;
  left: -3119px;
  bottom: -2308px;
  transform: scaleX(-1);
}


/* ------------------------------------------------------------
   Page — the centred grid, in normal flow
   ------------------------------------------------------------ */
.page {
  position: relative;
  z-index: 1;
  width: var(--grid-width);
  min-height: 100vh;
  margin-inline: auto;
  display: flex;
  flex-direction: column;
}

.logo {
  position: absolute;
  top: var(--logo-top);
  left: var(--gutter);
  display: block;
  width: 150px;
}

.logo img { display: block; width: 100%; height: auto; }

/* Only the compact layout pins the logo, so only the compact layout needs
   paper behind it. */
.masthead { display: none; }

/* Two columns: the copy on the left, nav + news on the right. Both are in
   flow, so the page is as tall as whichever runs longer. */
.layout {
  flex: 1 0 auto;
  display: grid;
  grid-template-columns: var(--content-width) 1fr;
  grid-template-rows: auto 1fr;
  padding: var(--title-top) var(--nav-right) 0 var(--gutter);
}

/* Placed explicitly: the copy runs down the whole left track, the sidebar and
   the side column stack in the right one — so the side column can leave the
   sidebar's box and still stand where the news list stands. It has to leave
   it, because below 1024 the sidebar becomes the menu overlay and contextual
   information does not belong in a menu. */
.content {
  grid-column: 1;
  grid-row: 1 / 3;
  /* A grid item's automatic minimum size is its content, so without this a
     table too wide to wrap would widen the column instead of scrolling. */
  min-width: 0;
  /* Editors write bare URLs, and a browser will not break one: at 320 a link
     like www.example.ch/Portals/0/PDF/support/file.pdf is wider than the whole
     column and takes the page sideways with it. Breaks only what cannot fit. */
  overflow-wrap: break-word;
}
.sidebar { grid-column: 2; grid-row: 1; }

/* The copy under the title starts level with the menu: the capitals of its
   first line stand on the same top edge as the capitals of the first nav item.
   Line boxes would not do — the two faces sit at different heights in their
   own lines — so both sides are measured to the cap height, in em of their own
   size at --line:
     Roboto Flex 800  0.2895em  (half-leading 0.0837 + ascent-to-cap 0.2058)
     IBM Plex Sans    0.344em   (half-leading 0.022  + ascent-to-cap 0.322)
   --copy-gap is then the space from the title's foot to the first block of
   copy. It is used on that block rather than on the title, because the 0.344em
   has to be in the block's own size — a lead, a heading and a plain paragraph
   each land differently otherwise. A title that wraps pushes the copy down
   with it; there is no nav line to meet that far down. */
.content {
  --copy-gap: calc(
    var(--nav-top) + var(--nav-size) * 0.2895
    - var(--title-top) - var(--title-line)
    - 0.344em
  );
}

.page-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--title-size);
  font-weight: 800;
  line-height: var(--line);
  color: var(--eye-red);
}

/* The first block of copy, reached through the content element's frame and
   its header — the margin collapses up through both to the title. Only the
   text element itself may carry it: the frame and the header are at body
   size, and collapsed margins keep the larger, so theirs would win. The bare
   sibling is the news detail, whose date follows the title directly. */
.page-title + :not(.frame),
.page-title + .frame > :first-child:not(header),
.page-title + .frame > header:first-child > :first-child {
  margin-top: var(--copy-gap);
}

/* Whatever a content element's media holds — an image, a video file, a
   YouTube or Vimeo embed — may arrive here at the width it was rendered at,
   wider than the reading column. Held to the column. */
.content img,
.content video,
.content iframe {
  display: block;
  max-width: 100%;
  margin-bottom: 1.334em;
}

.content img,
.content video { height: auto; }

/* An embed is the exception: the renderer writes a width attribute and no
   height, and an iframe with no height is 150px tall by default — so it gets
   the ratio here rather than a height it would have to be told. */
.content iframe {
  width: 100%;
  aspect-ratio: 16 / 9;
  height: auto;
  border: 0;
}

.content p { margin: 0 0 1.334em; }
.content p:last-child { margin-bottom: 0; }

/* A date — a header's, a news item's — is data rather than copy, so it is
   set like the news list: mono, one step down. */
.content .date {
  font-family: var(--font-mono);
  font-size: var(--news-size);
  line-height: var(--news-line);
}

/* The editor's "Lead" style: the opening paragraph, set between the body and
   the title. 28 on the desktop composition at every width; the compact block
   runs it down from there. Its margin is the paragraph rhythm in its own em,
   so the step down to the copy after it is a lead-sized one. */
.content p.lead { font-size: var(--lead-size); }

/* The editor's headings, on the body's scale rather than the browser's 1.5em,
   which on a phone set an h2 above the page title it belongs under. Steps of
   a third of the line: 24 / 21 / 18 on the body at 18. */
.content h2,
.content h3,
.content h4 {
  font-weight: 700;
  line-height: var(--line);
}

.content h2 { font-size: calc(var(--body-size) * 1.334); }
.content h3 { font-size: calc(var(--body-size) * 1.167); }
.content h4 { font-size: var(--body-size); }

/* A list belongs to the sentence that introduces it, so it sits closer to that
   than to whatever follows. The space above is the introducing paragraph's own
   margin, halved — the list's top margin cannot do it, because adjacent
   margins collapse to the larger of the two and the paragraph's would win. The
   space below is the paragraph rhythm, unchanged. */
.content ul,
.content ol {
  margin: 0 0 1.334em;
}

.content p:has(+ ul),
.content p:has(+ ol) {
  margin-bottom: 0.667em;
}

/* Items that run over several lines blur into one block at the bare line
   height. Half the paragraph rhythm between them — the same step as from the
   introducing sentence — keeps each one apart and the list still one list. A
   nested list sits inside its item at that step too, not at a paragraph's.

   This and the list styles below are for the editor's lists, which carry no
   class; a list a block builds for its own layout, like the teaser graph's
   commits, keeps its own. */
:is(.content ul, .content ol):not([class]) > li + li { margin-top: 0.667em; }

.content li > ul:not([class]),
.content li > ol:not([class]) { margin: 0.667em 0 0; }

.content li::marker { color: var(--eye-red); }

/* A dot at every depth. The browser turns the bullets of a nested list into
   rings, which in red would read as the numbered items' rings below. */
.content ul:not([class]) { list-style-type: disc; }

/* Numbered items: the number in a red ring, drawn in the hairline of the
   accordion's nodes at the size of a line, centred on the item's first
   line. The browser's own list-item counter, so `start` and `reversed` from
   the editor still count. Top-level lists only: a numbered list nested in
   an item keeps plain numbers. */
.content :not(li) > ol:not([class]) {
  --ol-disc: calc(var(--body-size) * 1.25);

  list-style: none;
  padding-left: calc(var(--ol-disc) + 0.667em);
}

.content :not(li) > ol:not([class]) > li { position: relative; }

/* Already indented past the rings, a bulleted list in a numbered item takes
   half the browser's 40px on a phone, where the column cannot spare it. */
@media (max-width: 1023px) {
  .content ol:not([class]) > li > ul:not([class]) { padding-left: 20px; }
}

.content :not(li) > ol:not([class]) > li::before {
  content: counter(list-item);
  position: absolute;
  top: calc(var(--body-size) * (var(--line) - 1.25) / 2);
  left: calc(-1 * var(--ol-disc) - 0.667em);
  display: grid;
  place-items: center;
  width: var(--ol-disc);
  height: var(--ol-disc);
  border-radius: 50%;
  border: 1px solid var(--eye-red);
  color: var(--eye-red);
  font-size: calc(var(--body-size) * 0.667);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

.content a {
  text-decoration: underline;
  text-underline-position: from-font;
}


/* ------------------------------------------------------------
   Tables — the editor's, from the rich text field
   ------------------------------------------------------------

   CKEditor wraps every table in <figure class="table">, which is the box the
   browser gives 40px of side margin; the table is copy like any other, so it
   starts and ends on the column's own edges.

   Borders in currentColor rather than --ink: the same table set in the side
   column stands on the red, where its rules have to be white with the type.

   Responsive in two stages. The table is 100% wide, so it fits the column and
   the cells wrap — that alone handles most tables at any width. When the
   content cannot shrink any further (a long address, more columns than the
   screen has room for), the table pushes past its min-content width and the
   figure scrolls it: the reading column keeps its edges, and only the table
   moves. Nothing is cut off and nothing overflows the page.
   ------------------------------------------------------------ */
figure.table {
  max-width: 100%;
  margin-inline: 0;
  overflow-x: auto;
}

figure.table table {
  width: 100%;
  border-collapse: collapse;
  /* The outer horizontal rules: this one at the top, the last row's own
     border-bottom at the foot. The vertical edges stay open. */
  border-top: 1px solid currentColor;
}

figure.table th,
figure.table td {
  padding: 0.5em 0.75em;
  border-right: 1px solid currentColor;
  border-bottom: 1px solid currentColor;
  text-align: left;
  vertical-align: top;
  /* Breaks a word only when it would otherwise not fit, which keeps the table
     inside the column for one more class of content before it has to scroll. */
  overflow-wrap: break-word;
}

/* The first and last cells of every row carry the table's outer edge: no rule
   there, and no padding either, so the table's type lines up with the copy
   above and below it. */
figure.table tr > *:first-child { padding-left: 0; }

figure.table tr > *:last-child {
  padding-right: 0;
  border-right: 0;
}


/* ------------------------------------------------------------
   Connector — unchanged from the fixed variant, all top-anchored
   ------------------------------------------------------------ */
.connector {
  --nav-active: 0;            /* overridden by js/connector.js */
  /* The line the connector runs down to: the middle of the active menu item,
     plus the optical nudge. The formula is the fallback — it can only count
     whole items of one size, which stops being true as soon as an item has
     subpages under it, so connector.js measures the item and publishes
     --nav-mid instead. */
  --nav-mid: calc(
    var(--nav-top)
    + var(--nav-active) * var(--nav-line)
    + var(--nav-line) / 2
  );
  --nav-target: calc(var(--nav-mid) + var(--nav-nudge));
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.connector span { position: absolute; }

.connector__bullet,
.connector__target {
  width: var(--bullet);
  height: var(--bullet);
  border-radius: 50%;
}

.connector__bullet {
  top: calc(var(--title-mid) - var(--bullet) / 2);
  left: calc(var(--gutter) - 28px);
  background: var(--eye-red);
}

.connector__stub {
  top: calc(var(--title-mid) + var(--bullet) / 2);
  left: calc(var(--gutter) - 21px);
  width: 1px;
  height: calc(var(--rule-top) - var(--title-mid) - var(--bullet) / 2);
  background: var(--ink);
}

/* Black up to 91px past the crossing, white from the crossing itself, painted
   over the black. --rule-split tracks the crossing as the "E" moves. */
.connector__rule {
  top: var(--rule-top);
  left: calc(var(--gutter) - 21px);
  right: calc(var(--grid-width) - var(--rule-split) - 91px);
  height: 1px;
  background: var(--ink);
}

.connector__rule-on-red {
  top: var(--rule-top);
  left: var(--rule-split);
  right: var(--connector-x);
  height: 1px;
  background: var(--paper);
}

.connector__drop {
  top: var(--rule-top);
  right: var(--connector-x);
  width: 1px;
  background: var(--paper);
  height: calc(var(--nav-target) - var(--rule-top));
}

.connector__target {
  right: calc(var(--connector-x) - var(--bullet) / 2);
  background: var(--paper);
  top: calc(var(--nav-target) - var(--bullet) / 2);
}

/* Nothing in the menu is active — the home page, which is deliberately not an
   item, and a page kept out of the menu directly under it (one further down
   is stood in for by its section). connector.js sets the class, since
   it is the one that knows, and which of the two it sets depends on whether
   there is a news list to hand the line on to.

   With one: the rule turns down at the page's right edge — the column the news
   items' own vertical wire runs in — and stops on the first item's line, where
   that wire begins. The two are then one line, and the list carries the
   connector on down past its own items.

   Without one, there is nothing to point at at all, so the rule does not turn
   down: it carries straight on to the edge of the screen and neither the drop
   nor its bullet is drawn. */
.connector.is-keyed-to-news {
  /* The news wires meet their items at the middle of the line, no nudge. */
  --nav-target: var(--nav-mid);
}

.connector.is-keyed-to-news .connector__rule-on-red,
.connector.is-keyed-to-news .connector__drop { right: 0; }

.connector.is-keyed-to-news .connector__target { display: none; }

.connector.is-unkeyed .connector__rule-on-red { right: calc(-1 * var(--bleed)); }

/* A subpage is set like a news item, so it is met like one: at the middle of
   its line and without --nav-nudge, which is the optical correction the
   display weight above it needs and this size does not (the news wires do not
   use it either). */
.connector.is-keyed-to-subpage { --nav-target: var(--nav-mid); }

.connector.is-unkeyed .connector__drop,
.connector.is-unkeyed .connector__target { display: none; }


/* ------------------------------------------------------------
   Right column
   ------------------------------------------------------------ */
.sidebar {
  width: max-content;
  max-width: var(--news-width);
  margin-left: auto;
  /* the nav starts on 314 while the copy starts on --title-top */
  padding-top: calc(var(--nav-top) - var(--title-top));
  text-align: right;
}

.news { margin-top: var(--sidebar-gap); }

/* The side column (the "Default" page layout's second column) stands where
   the news list does on the other layouts, set the same way: paper on the
   red, right-aligned with the nav, at the news size. */
.side {
  grid-column: 2;
  grid-row: 2;
  align-self: start;
  max-width: var(--news-width);
  margin-top: var(--sidebar-gap);
  margin-left: auto;
  font-size: var(--news-size);
  line-height: var(--news-line);
  color: var(--paper);
  text-align: right;
}

/* Its headings take the copy's steps (see .content h2) from its own, smaller
   text rather than the browser's 1.5em: 21 / 19 / 16 on the side's 16. In em,
   so on a phone, where the side takes the body size, they meet the copy's
   own. The top margin keeps the heading where the browser's put it; the space
   below is half that, so the heading sits with what it introduces. */
.side :is(h2, h3, h4) {
  margin: 1em 0 0.5em;
  font-weight: 700;
  line-height: var(--line);
}

.side h2 { font-size: 1.334em; }
.side h3 { font-size: 1.167em; }
.side h4 { font-size: 1em; }

.mainnav ul { margin: 0; padding: 0; list-style: none; }

.mainnav li {
  font-family: var(--font-display);
  font-size: var(--nav-size);
  font-weight: 800;
  line-height: var(--nav-line);
  color: var(--paper);
}

.mainnav a { text-decoration: none; }
.mainnav a:hover { text-decoration: underline; }

/* The subpages of the current section, shown under their parent and only
   there. Set like the news list — the menu item is the heading, these are what
   it holds — with a little air above so they read as its contents rather than
   as more menu. */
.subnav { padding: 0; list-style: none; }

/* Both margins here, not on .subnav: `.mainnav ul` above is the more specific
   selector and would win. Tight to the item they belong to, a line's worth of
   air before the next one — so they read as that item's contents and not as a
   list of their own. */
.mainnav .subnav {
  margin: 0 0 calc(var(--news-line) / 2);
}

.mainnav .subnav li {
  position: relative;
  font-size: var(--news-size);
  font-weight: 500;
  line-height: var(--news-line);
}


/* ------------------------------------------------------------
   News
   ------------------------------------------------------------ */
.news ul { margin: 0; padding: 0; list-style: none; }

.news li {
  position: relative;
  font-family: var(--font-mono);
  font-size: var(--news-size);
  font-weight: 500;
  line-height: var(--news-line);
  color: var(--paper);
}

.news li + li { margin-top: var(--news-gap); }

.news a { text-decoration: none; }
.news a:hover { text-decoration: underline; }

/* Vertical link down to the next item's wire, measured off this item's own
   height so wrapped headlines stay connected. In the same column as the
   connector's drop, which runs down to the first item at every width, so it
   carries that line on at every width too — including the narrow windows
   where there is no bleed beside the grid. */
.news li::after {
  content: "";
  position: absolute;
  right: calc(-1 * var(--nav-right));
  top: calc(var(--news-line) / 2);
  width: 1px;
  height: calc(100% + var(--news-gap));
  background: var(--paper);
}

.news li:last-child::after { content: none; }

/* Covers the whole item so its two marks can hang off it; pointer-events so
   the link underneath stays clickable. */
.news__wire { position: absolute; inset: 0; pointer-events: none; }

.news__wire::before,
.news__wire::after {
  content: "";
  position: absolute;
  top: calc(var(--news-line) / 2);
  background: var(--paper);
}

.news__wire::before {
  right: calc(var(--connector-x) - 6px - var(--nav-right));
  width: var(--bullet);
  height: var(--bullet);
  margin-top: calc(var(--bullet) / -2);
  border-radius: 50%;
}

.news__wire::after {
  right: calc(-1 * var(--nav-right));
  width: var(--connector-x);
  height: 1px;
}

.news li:first-child .news__wire::after {
  right: calc(-1 * var(--nav-right) - var(--bleed));
  width: calc(var(--connector-x) + var(--bleed));
}

/* Important news — the bullet sends out a slow ring. */
.news li.is-important .news__wire::before {
  animation: news-ping 2.4s cubic-bezier(0, 0, 0.2, 1) infinite;
}

@keyframes news-ping {
  0%   { box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.6); }
  70%  { box-shadow: 0 0 0 13px rgba(255, 255, 255, 0); }
  100% { box-shadow: 0 0 0 0 rgba(255, 255, 255, 0); }
}

@media (prefers-reduced-motion: reduce) {
  .news li.is-important .news__wire::before {
    animation: none;
    box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.4);
  }
}


/* ------------------------------------------------------------
   Footer — end of the document, not the bottom of the window
   ------------------------------------------------------------ */
.pagefoot {
  align-self: flex-end;
  max-width: var(--foot-width);
  /* Right margin is --nav-right, the same edge .layout's right track ends on,
     so the footer's last character lines up with the nav, the news and the
     side column above it rather than with the connector's bullet column. */
  margin: var(--foot-top) var(--nav-right) var(--foot-bottom) 0;
  text-align: right;
}

.pagefoot p {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--foot-size);
  font-weight: 400;
  line-height: var(--line);
  color: var(--paper);
}

.pagefoot a {
  text-decoration: underline;
  text-underline-position: from-font;
}


/* ============================================================
   Menu button — the two-dot mark from the Figma component
   "Menu mobile" (74 x 74).

   Proportions are the component's: a circle with a 5-unit red stroke
   holding two 14-unit dots on centres 10 either side of the middle. The
   viewBox is left at the drawn 74 and the button scaled down to r=28, so
   every one of those relationships survives at 56px — the stroke lands
   at 3.8, the dots at 10.6.

   Open, the dots slide into the middle and hand over to an X. The ring
   stays put through the whole thing — it is the button, the mark inside
   it is only the state.

   The one pinned thing on this page, and it has to be: a control that
   opens the navigation is no use if it scrolls away with the masthead.
   The white fill is what lets it sit over the copy instead of reserving
   a strip of screen nothing else may use.
   ============================================================ */
.navtoggle {
  position: fixed;
  z-index: 4;                 /* over .sidebar (3) — it has to close it */
  right: calc(var(--fab-inset) + env(safe-area-inset-right, 0px));
  bottom: calc(var(--fab-inset) + env(safe-area-inset-bottom, 0px));
  display: none;              /* desktop has a real sidebar; see below */
  width: var(--fab-size);
  height: var(--fab-size);
  padding: 0;
  border: 0;
  border-radius: 50%;
  color: var(--eye-red);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  background: none;
  filter: drop-shadow(0 2px 10px rgba(0, 0, 0, 0.14));
}

/* No focus ring. Any ring drawn around this button reads as a second circle
   stacked on the artwork rather than as a state, so there is none. The
   dots-to-X change is the only feedback the button gives. */
.navtoggle:focus,
.navtoggle:focus-visible { outline: none; }

.navtoggle__icon { display: block; width: 100%; height: 100%; }

.navtoggle__ring {
  fill: var(--paper);
  stroke: currentColor;
  stroke-width: 5;
}

.navtoggle__dot { fill: currentColor; }

.navtoggle__x {
  stroke: currentColor;
  stroke-width: 5;
  stroke-linecap: round;
}

.navtoggle__dot,
.navtoggle__x {
  transform-origin: 37px 37px;
  transition: opacity 0.18s ease, transform 0.18s ease;
}

/* Closed state: the X is collapsed into the centre, waiting. */
.navtoggle__x { opacity: 0; transform: scale(0.5); }

.is-nav-open .navtoggle__dot { opacity: 0; }
.is-nav-open .navtoggle__dot--top { transform: translateY(10.5px); }
.is-nav-open .navtoggle__dot--bottom { transform: translateY(-10.5px); }
.is-nav-open .navtoggle__x { opacity: 1; transform: scale(1); }


/* ============================================================
   Compact layout — below 1024
   ============================================================

   What goes, and why
   ------------------
   The red field is not decoration; it is the ground the nav, the news
   and the footer are set on. Once there is no room for that column
   there is nothing for the field to hold, so shape and field leave
   together rather than the field shrinking into a stripe. The page is
   then white, and the footer — still the last block of the document,
   still in flow — turns from white type to ink and crosses to the
   left gutter with everything else.

   What comes back
   ---------------
   The same .sidebar element, unchanged in markup, becomes a
   full-viewport red panel behind the menu button. The nav is still at
   the top, the news still below it, the wires still run out to the
   right edge — the composition survives, it is just the whole screen
   now instead of a column of it.
   ============================================================ */
@media (max-width: 1023px) {

  :root {
    /* Same clamp form as the desktop block, run over 360 -> 1024 instead;
       each slope is (value at 1024 - value at 360) / 664. */
    --gutter: clamp(24px, calc(24px + (var(--viewport) - 360px) * 0.0722892), 72px);
    --logo-top: clamp(24px, calc(24px + (var(--viewport) - 360px) * 0.0677711), 69px);

    /* Two masthead sizes and the one currently drawn.

       The mark is fixed to the window down here (see .logo below), so it is
       the only thing on the page that is still there after a screen of
       scrolling — which makes it worth opening at a size that reads as the
       masthead rather than as a corner mark, and worth settling back to the
       smaller one once the reading has started. --logo-width is what
       everything reads; masthead.js swaps which of the two it holds by putting
       .is-scrolled on <body>.

       1.4 rather than a clamp of its own so the two sizes keep their ratio all
       the way across the range; the rest size is the one the 1024 frame
       draws. */
    --logo-width-rest: clamp(104px, calc(104px + (var(--viewport) - 360px) * 0.0692771), 150px);

    /* The opened size is the settled one plus four tenths of it — except that
       the growth is itself tapered away below 360, so at 320 the two sizes are
       the same and the mark simply never grows. A 4" screen has 568px of
       height for the whole composition; the one thing it cannot spend them on
       is a masthead that has to shrink to make room for the reading. Same
       shape as --nav-size and --news-gap: a base plus a term that is inert at
       360 and above. */
    --logo-width-full: calc(
      var(--logo-width-rest)
      + clamp(0px, (var(--viewport) - 320px) * 1.04, var(--logo-width-rest) * 0.4)
    );
    --logo-width: var(--logo-width-full);

    /* Paper below the mark before the band under it stops. Constant: it is
       breathing room around a fixed object at the top of the window, not part
       of the text grid. */
    --masthead-pad: 20px;

    /* How far the band — and the menu overlay — carry on *above* the top of
       the viewport.

       iOS Safari lays the document out taller than the area a fixed element
       is measured against: the page paints on up under the status bar, while
       `top: 0` lands below it. A band that stops at the viewport's top edge
       therefore leaves a strip in which the copy scrolls past above the logo,
       greyed by the status bar's tint but perfectly legible. The same strip
       would show white above the red menu panel.

       Nothing depends on the exact number — it is off-screen paint — so it is
       one generous constant rather than env(safe-area-inset-top), which is 0
       in Safari's normal (non-fullscreen) chrome and would not have covered
       this. 200 also swallows any rubber-band gap at the top of the scroll. */
    --masthead-rise: 200px;

    /* Where the connector's rule comes to rest once the page has moved: one
       --masthead-pad below the settled mark, with another below the rule
       before the paper ends. The mark settles long before the rule gets this
       far up, so this reads --logo-width-rest and not the current width. */
    --connector-park: calc(
      var(--logo-top) + var(--logo-width-rest) * 0.5266667 + var(--masthead-pad)
    );

    /* The copy starts clear of the *opened* mark, not the settled one, and the
       air between the two roughly doubles: at 360 the logo's foot is at 101 and
       the title at 152. Still 232 at 1024, so nothing steps at the breakpoint.

       The fall below 360 is the other half of --logo-width-full's: with no
       growth left to clear at 320 the same 51px of air puts the title at 130,
       and holding it at 152 would have spent 22 of the screen's 568 on white
       that is only there for a bigger mark that is no longer drawn. */
    --title-top: calc(
      clamp(152px, calc(152px + (var(--viewport) - 360px) * 0.1204819), 232px)
      - clamp(0px, (360px - var(--viewport)) * 0.55, 22px)
    );

    --title-size: clamp(23px, calc(23px + (var(--viewport) - 360px) * 0.0075301), 28px);
    --body-size: 16px;
    /* 20 at 360 -> 28 at 1024, so it meets the desktop value at the breakpoint
       and stays under the title (23) on a phone, where 28 would outweigh it. */
    --lead-size: clamp(20px, calc(20px + (var(--viewport) - 360px) * 0.0120482), 28px);
    /* Body and news hold the sizes the desktop scale ends on at 1024: there is
       no smaller step that still reads on a phone. */
    --news-size: 15px;
    --foot-size: 13px;
    /* The overlay has the whole screen, so the nav gets *bigger* here than it
       is at the 1024 desktop floor, not smaller — up to 360. Below that it
       gives some of it back: 320 x 568 (an SE, a 5s) is a real part of the
       audience, and there the overlay has to hold five nav lines, four news
       lines and the button without the list running under it.

       Written as one term plus a rise and minus a fall rather than as a second
       breakpoint, so the whole range stays a single expression: 28 at 360, 34
       at 1024, 24 at 320. The fall's clamp is inert at 360 and above, where
       (360 - viewport) is negative. The same shape is used for --news-gap
       below; anything else that has to give way on a 4" screen belongs here
       too, not in a media query of its own. */
    --nav-size: calc(
      28px
      + clamp(0px, (var(--viewport) - 360px) * 0.0090361, 6px)
      - clamp(0px, (360px - var(--viewport)) * 0.1, 4px)
    );

    /* Air between the news lines. It is --news-line everywhere else — the list
       is set on its own leading — and that is exactly what there is no room for
       at 320, where the list is the block that would be pushed off the bottom.
       Down to 13.3 there (a hair under two thirds of the line), untouched at
       360 and above. The wires between the bullets read --news-gap for their
       length, so they follow. */
    --news-gap: calc(
      var(--news-line) - clamp(0px, (360px - var(--viewport)) * 0.2, 8px)
    );

    /* How far the nav's first line of capitals sits below the logo's
       strapline. On the desktop composition the two hand over on one line;
       given a whole screen the overlay wants the mark to read as a masthead
       with the menu under it, not as the first item of the list. */
    --nav-lead: clamp(28px, calc(28px + (var(--viewport) - 360px) * 0.0301205), 48px);

    /* The rule under the heading is no longer a fixed 285: it hangs off the
       title's own line box, so it keeps sitting between the heading and the
       first line of copy as both shrink. 16 / 31 reproduce the frame's
       spacing at 1024, where this block takes over.

       Both terms are the same 16px below the title — the first below one line
       of it, the second below however many lines it actually took. They agree
       for a one-line title; for a wrapped one the measured edge wins. This is
       where it matters: at 320 a title of any length wraps, and the rule was
       striking through its second line. */
    --rule-top: max(
      calc(var(--title-top) + var(--title-line) + 16px),
      calc(var(--title-bottom) + 16px)
    );

    /* Right edge of the nav and news type in the overlay, and the bullet/wire
       column outboard of it. --nav-right has to stay clear of --connector-x
       plus the bullet or the wires would run through the words. */
    --nav-right: calc(var(--gutter) + 32px);
    --connector-x: 22px;

    --foot-top: 56px;

    /* The button's own inset. Deliberately not --gutter: the thumb zone is the
       corner of the screen, not the text grid, so this stays near-constant
       (24 -> 36) while the gutter triples. */
    --fab-inset: clamp(24px, calc(24px + (var(--viewport) - 360px) * 0.0180723), 36px);

    /* ---- where the nav starts, on the overlay ------------------
       Two ink edges that have to meet, so both are read off the things that
       draw them rather than eyeballed.

       Bottom of the logo's strapline. "EYE Communications AG" is its own
       nested <svg> in the artwork at y 67.005, height 10.1326, and the string
       has no descenders — so the tight box's bottom edge is its baseline, at
       77.1376 in a 150-unit-wide viewBox. The image is scaled by its width,
       so one viewBox unit is --logo-width / 150: 77.1376 / 150 = 0.5142507.

       Declared on .sidebar rather than here for the reason the band's height
       is: it has to substitute the mark's *current* width, which is set on
       <body>, so it has to be read below <body>. */

    /* Top of a nav item's capitals, measured from the top of its line box:
       the half-leading, plus the room the font leaves between its ascender
       and its cap height. Roboto Flex is 0.9286em ascent, 0.25em descent
       (1.1786em together) and 0.7241em cap, so the second term is 0.2045em.
       Written in two terms rather than collapsed to one coefficient so it
       still holds if --line changes. */
    --nav-cap-drop: calc(
      (var(--nav-line) - var(--nav-size) * 1.1786) / 2
      + var(--nav-size) * 0.2045
    );
  }

  /* No room for the column they back. */
  .e-shape--right,
  .e-field { display: none; }

  /* Fixed, and fixed at every scroll position, not only while the menu is up:
     the mark is the one thing that has to stay reachable on a page this long
     and this narrow, so it sits on the window and the copy passes underneath
     it. --grid-width is the full viewport down here, so .page's left edge is
     the window's and the same `top`/`left` land on the same spot they did in
     the document.

     ::before is the ground that makes passing underneath legible: a band of
     paper from the top of the window to --masthead-pad below the mark, right
     across the width. The compact page is white — the field and the shape are
     both gone by here — so at rest the band shows as nothing at all; it only
     ever has the copy sliding under it to hide. */
  .logo {
    position: fixed;
    width: var(--logo-width);
    /* Over the overlay (3), alongside the button (4). */
    z-index: 4;
    /* The identity filter is only here to give the switch below something to
       interpolate from — `none` is not a filter list and will not animate. */
    filter: brightness(1) invert(0);
    transition: width 0.18s ease, filter 0.18s ease;
  }

  /* The band is its own fixed element rather than a pseudo of .logo, and that
     is deliberate: .logo carries a filter, which puts anything inside it into
     a composited, filtered layer, and on iOS that layer is not a reliable way
     to paint into the strip above the top of the viewport. Nothing is inside
     it — it is one rectangle of paper — so it costs a div and answers the
     question. */
  .masthead {
    display: block;
    position: fixed;
    top: calc(-1 * var(--masthead-rise));
    left: 0;
    width: var(--viewport);
    /* The artwork is 150 x 79 and scales by its width, so the mark's height is
       the width times 79/150. Multiplied out here rather than kept as a
       variable on :root: a custom property substitutes where it is *declared*,
       so a --logo-height up there would freeze the full width and the band
       would never settle with the mark. */
    height: calc(
      var(--masthead-rise) + var(--logo-top)
      + var(--logo-width) * 0.5266667 + var(--masthead-pad) * 2
    );
    z-index: 2;                 /* over the copy, under the overlay (3) */
    background: var(--paper);
    transition: height 0.18s ease;
  }

  /* Nothing to hide on the red, and it would be a white slab. */
  .is-nav-open .masthead { display: none; }

  /* Once the page has moved the mark settles to the size the rest of the
     compact layout is drawn against. On <body> rather than :root so it can
     override the value the media query sets there. */
  body.is-scrolled { --logo-width: var(--logo-width-rest); }

  /* On the menu layer the logo stays exactly where it is — it is already on the
     window — and only changes colour.

     White by flattening rather than by colour: the mark carries its own red
     and black inside the SVG, so there is no fill to override from out here.
     brightness(0) takes every channel to black and invert(1) takes it to
     white, and neither touches the transparent ground. */
  .is-nav-open .logo { filter: brightness(0) invert(1); }


  /* One column, capped so the measure stays readable on a tablet — 640 is
     close to the desktop reading column's own ceiling. The sidebar leaves the
     grid entirely (a fixed item is not a grid item), so nothing needs to be
     said about the second track. */
  .layout {
    grid-template-columns: minmax(0, 640px);
    padding: var(--title-top) var(--gutter) 0;
  }

  /* No menu beside the copy to line up with. */
  .content { --copy-gap: 31px; }

  /* One column again; the side column simply follows the copy. */
  .content,
  .side { grid-column: auto; grid-row: auto; }

  /* Contextual information after the copy, on the paper now rather than on the
     red, and keyed by the mark below so it reads as a further section rather
     than as a footnote. */
  .side {
    max-width: none;
    margin: calc(var(--sidebar-gap) / 2) 0 0;
    font-size: inherit;
    line-height: inherit;
    color: var(--ink);
    text-align: left;
  }

  /* ---- the block mark --------------------------------------
     What divides one block from the next once everything is in one column: a
     rule coming in from the edge of the screen and a bullet where the text
     starts. Two blocks carry it, the side column and the footer, because both
     follow the reading rather than belong to it — and on paper, with no red
     column left to set them on, nothing else says where one stops and the
     other begins.

     It is the page title's connector turned around: there the rule leaves the
     bullet for the right edge of the screen, here it arrives from the left and
     stops at it. A rule crossing the full width would have run out into the
     white space below the copy, pointing at nothing. */
  .side,
  .pagefoot {
    position: relative;
    padding-top: 31px;
  }

  .side::before,
  .side::after,
  .pagefoot::before,
  .pagefoot::after {
    content: "";
    position: absolute;
  }

  .side::before,
  .pagefoot::before {
    top: calc(-1 * var(--bullet) / 2);
    left: 0;
    width: var(--bullet);
    height: var(--bullet);
    border-radius: 50%;
    background: var(--eye-red);
  }

  /* Both blocks start at the text column's left edge, so --gutter is x 0 and
     the rule ends exactly where the bullet starts. */
  .side::after,
  .pagefoot::after {
    top: 0;
    left: calc(-1 * var(--gutter));
    width: var(--gutter);
    height: 1px;
    background: var(--ink);
  }

  /* Connector, compressed.

     On desktop the bullet marks the page title and the rule carries on across
     the page onto the red. With the red gone and the gutter down to 24px there
     is no longer 28px of margin to hang a bullet in, so the motif folds up: the
     bullet moves onto the near end of the rule, aligned with the title and the
     copy, and the rule runs unbroken off the right edge. Same three marks, one
     fewer turn. */
  /* And it sticks. The rule starts under the page title, travels up with the
     copy, and comes to rest inside the masthead's paper, --masthead-pad below
     the settled mark — so from then on the copy passes under a line rather
     than under the logo itself, and the pinned mark has a foot instead of a
     soft edge. Nothing new is drawn for it: it is the same rule, parked.

     Sticky rather than a switch to fixed on .is-scrolled, because a switch
     would teleport the rule ~100px the moment the class landed. Sticky needs
     the box to be in flow, which the desktop connector is not, so down here it
     becomes a zero-height flex item of .page carrying the marks: the top
     margin puts it where the rule belongs and the bottom margin gives the same
     distance back, so its contribution to the column is nil and .layout starts
     exactly where it did. `inset: auto` first, to clear the desktop rule's
     `inset: 0` — on a sticky box those four are the constraints, not offsets. */
  /* The masthead and the parked rule are pinned over the top of the window;
     an anchor has to land a line below them, not under them. */
  html {
    scroll-padding-top: calc(var(--connector-park) + var(--body-size) * var(--line));
  }

  .connector {
    inset: auto;
    position: sticky;
    top: var(--connector-park);
    height: 0;
    margin: var(--rule-top) 0 calc(-1 * var(--rule-top));
    z-index: 2;               /* with the band; later in the document, so over it */
  }

  /* Both marks are now measured from the rule's own line rather than from the
     top of the page. */
  .connector__bullet {
    top: calc(-1 * var(--bullet) / 2);
    left: var(--gutter);
  }

  .connector__rule {
    top: 0;
    left: calc(var(--gutter) + var(--bullet));
    right: 0;
  }

  .connector__stub,
  .connector__rule-on-red,
  .connector__drop,
  .connector__target { display: none; }

  /* ---- the overlay ---------------------------------------- */
  .sidebar {
    --logo-text-bottom: calc(var(--logo-top) + var(--logo-width) * 0.5142507);

    position: fixed;
    inset: 0;
    z-index: 3;
    width: auto;              /* max-content would win over inset */
    max-width: none;
    margin-left: 0;
    display: flex;
    flex-direction: column;
    /* The nav's first line of capitals is set --nav-lead below where the
       logo's strapline stops: both edges are still read off the things that
       draw them, so the gap is a stated distance between two ink edges rather
       than a round number measured from nothing. No safe-area term on this
       one: it has to be measured from the same origin the logo is, and the
       logo's own `top` has none. */
    padding:
      calc(var(--logo-text-bottom) + var(--nav-lead) - var(--nav-cap-drop))
      var(--nav-right)
      calc(var(--fab-inset) + var(--fab-size) + 24px + env(safe-area-inset-bottom, 0px))
      var(--gutter);
    background: var(--eye-red);
    /* The same strip above the viewport, filled the same way. A shadow rather
       than a negative `top`, because `top` is what the padding below is
       measured from and the nav's first line has to keep meeting the logo's
       strapline. */
    box-shadow: 0 calc(-1 * var(--masthead-rise)) 0 var(--eye-red);
    overflow-y: auto;
    overscroll-behavior: contain;

    /* Fast fade — 180ms, in and out. visibility is what keeps the panel out of
       the tab order while it is down, delayed on the way out so the fade is
       still visible; pointer-events keeps the page underneath inert while it
       is up. */
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity 0.18s ease, visibility 0s linear 0.18s;
  }

  .is-nav-open .sidebar {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transition: opacity 0.18s ease, visibility 0s;
  }

  .sidebar:focus { outline: none; }   /* focused programmatically on open */

  /* The active item gets the news list's connector: a bullet in the same
     column as theirs, and a wire out to the edge of the screen. On desktop
     this is the far end of the connector's long journey from the page title;
     here that journey is folded away, but the mark it arrives at stays, so the
     nav is still keyed the way everything else on the page is.

     Geometry deliberately mirrors .news__wire — same --connector-x column,
     same --nav-right offset out to the viewport edge — the only difference
     being the line it hangs off. Written out rather than shared with it
     because the news wire exists at both sizes and this one only here. */
  .mainnav .is-active { position: relative; }

  .mainnav .is-active::before,
  .mainnav .is-active::after {
    content: "";
    position: absolute;
    top: calc(var(--nav-line) / 2 + var(--nav-nudge));
    background: var(--paper);
  }

  /* A subpage is set on the smaller line and met at the middle of it, the way
     the news wires meet theirs — see .connector.is-keyed-to-subpage. */
  .mainnav .subnav .is-active::before,
  .mainnav .subnav .is-active::after {
    top: calc(var(--news-line) / 2);
  }

  .mainnav .is-active::before {
    right: calc(var(--connector-x) - 6px - var(--nav-right));
    width: var(--bullet);
    height: var(--bullet);
    margin-top: calc(var(--bullet) / -2);
    border-radius: 50%;
  }

  .mainnav .is-active::after {
    right: calc(-1 * var(--nav-right));
    width: var(--connector-x);
    height: 1px;
  }

  /* Free space collects between the two, so the news sits at the foot of the
     panel — the same relationship the frame draws, given a whole screen. */
  .news { margin-top: auto; }

  /* The panel is wide now; let the headlines use it. */
  .news { max-width: none; }

  /* The link from one item down to the next carries the connector's drop on
     past the list. With the drop folded away there is nothing for it to carry
     on, so each wire runs straight out to the edge, as the active nav item's
     does. */
  .news li::after { content: none; }

  /* Ink on white, and across to the left gutter now that the red column it was
     set on has gone. Still simply the last block of the document; the right
     margin only keeps it out from under the button. */
  .pagefoot {
    align-self: stretch;
    max-width: none;
    margin: var(--foot-top) calc(var(--fab-inset) + var(--fab-size) + 16px)
            calc(var(--foot-bottom) + env(safe-area-inset-bottom, 0px))
            var(--gutter);
    text-align: left;
  }

  .pagefoot p { color: var(--ink); }

  .navtoggle { display: block; }
}

/* The document must not scroll behind the overlay. overscroll-behavior stops
   the chaining; this stops a two-finger drag on the panel's own margins. */
body.is-nav-open { overflow: hidden; }


/* The crossfade carries the state change on its own; the dots' slide is the
   part that is only there for pleasure, so that is the part that goes. */
@media (prefers-reduced-motion: reduce) {
  .navtoggle__dot,
  .navtoggle__x { transition: opacity 0.18s ease; }
  .navtoggle__x { transform: none; }
  .is-nav-open .navtoggle__dot--top,
  .is-nav-open .navtoggle__dot--bottom { transform: none; }

  /* Same reading for the masthead: the size change is movement and goes, the
     colour change on the menu layer is not and stays. */
  .logo { transition-property: filter; }
  .masthead { transition: none; }
}
