/* chart-core stylesheet — VERSION 1.0.0
   The version is printed here on purpose. Twice now we have spent time on a
   fault that was already fixed, because the site was serving an older copy of
   this file. Open the stylesheet URL and read the first line to know what is
   actually running. */

/* CHART components — ships with the plugin, no manual paste needed. */

.chart-block {
  --chart-red: #E3000F;
  --chart-red-soft: #FDF0F1;
  --chart-red-line: #F8D5D8;
  --chart-ink: #15171A;
  --chart-g1: #5C6169;
  --chart-g2: #9AA0A8;
  --chart-rule: #E6E8EB;   /* internal hairlines: rows, dividers */
  --chart-edge: #C3CAD2;   /* outer edges of cards, tiles and panels.
                              Was #D3D9E0, which read only because every card
                              was topped by a photograph supplying its own hard
                              edge. Once project cards fitted their image on
                              white and news cards sat on a white page, the edge
                              vanished. Darkened here rather than per component,
                              so team tiles, the facts banner and the document
                              card move with it. */
  --chart-wash: #F7F8F9;
  --chart-r: 10px;
  /* The site was running two typefaces: Inter on Elementor headings, and
     whatever OceanWP supplies (Open Sans) on everything the plugin renders —
     so a project title and the text under it disagreed. Inter is the face in
     direction-c-refined.html, and Elementor already hosts it locally at 400
     through 700, so naming it here costs no extra request. */
  --chart-font: 'Inter', -apple-system, BlinkMacSystemFont, system-ui, 'Segoe UI', sans-serif;
  font-family: var(--chart-font);
  font-size: 16px;
  line-height: 1.55;
  color: var(--chart-ink);
}
/* The news body is rendered by Elementor's Post Content widget, outside any
   .chart-block, so it needs the family naming separately. */
.single-post .elementor-widget-theme-post-content,
.chart-article {
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, system-ui, 'Segoe UI', sans-serif;
}
.chart-block *, .chart-block *::before, .chart-block *::after { box-sizing: border-box; }
.chart-block img { max-width: 100%; display: block; }
.chart-block [hidden] { display: none !important; }

/* Defensive reset.
   These blocks sit inside whatever theme is active, and themes routinely
   underline every link and add margins to headings and lists. text-decoration
   propagates from the anchor down to its children and cannot be removed by
   the children, so it has to be beaten on the anchor itself — hence the
   !important, which is deliberate rather than lazy. */
.chart-block a,
.chart-block a:link,
.chart-block a:visited,
.chart-block a:hover,
.chart-block a:focus,
.chart-block a:active,
.chart-block a *,
.chart-block a *:hover {
  text-decoration: none !important;
  -webkit-text-decoration: none !important;
  text-decoration-line: none !important;
  -webkit-text-decoration-line: none !important;
  text-underline-offset: unset !important;
}

/* Colour belongs on the anchor ONLY.
   This used to be part of the selector above, which includes `.chart-block a *`
   and therefore scored (0,1,2) against every descendant — beating any single
   class that tried to set a colour inside a card. That is why the featured
   card's red eyebrow and Read more came out black, and it silently threatened
   eleven other rules (tags, dates, roles, institutes). Descendants inherit from
   the anchor on their own; forcing it on them buys nothing and breaks things. */
.chart-block a,
.chart-block a:link,
.chart-block a:visited { color: inherit; }

/* Some themes fake an underline with a bottom border, a box-shadow or a
   gradient background rather than text-decoration, so those have to go too.
   But a card IS an anchor. An earlier version of this rule applied to every
   anchor and quietly stripped border-bottom from all 29 cards — which is what
   the "white shade along the lower edge" turned out to be. It cost four rounds
   of debugging. Card containers are excluded by name; keep them excluded. */
.chart-block a:not(.chart-card):not(.chart-person):not(.chart-tile):not(.chart-feature):not(.chart-btn),
.chart-block a:not(.chart-card):not(.chart-person):not(.chart-tile):not(.chart-feature):not(.chart-btn) *:not(figure) {
  background-image: none !important;
  box-shadow: none !important;
  border-bottom: 0 !important;
}
/* :where() contributes NO specificity, so any component class beats this.
   Written as plain selectors it scored 0,1,1 and silently overrode every
   component rule that styles a bare element — .chart-yearhead, then
   .chart-boardhead, then .chart-boardblurb, each looking like a fresh bug.
   Do not "simplify" this back into ordinary selectors. */
:where(.chart-block h2,
       .chart-block h3,
       .chart-block ul,
       .chart-block li,
       .chart-block p,
       .chart-block figure) { margin: 0; padding: 0; }
.chart-block ul { list-style: none; }
.chart-block li::before, .chart-block li::marker { content: none; }
.chart-block button { font-family: inherit; }

/* Themes sometimes attach shadows, filters or pseudo-element overlays to
   links and figures. Any of those can smear a card's edge. */
.chart-block .chart-card::before,
.chart-block .chart-card::after,
.chart-block .chart-person::before,
.chart-block .chart-person::after { content: none !important; }
.chart-block figure { filter: none !important; box-shadow: none !important; }

/* ---------- filters ---------- */
.chart-filters { display: flex; flex-direction: column; gap: 10px; margin-bottom: 26px; }
.chart-frow { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.chart-flabel {
  font-size: 12px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--chart-g2); min-width: 74px;
}
.chart-chip {
  font: inherit; font-size: 13.5px; font-weight: 500;
  padding: 7px 14px; border-radius: 99px;
  border: 1px solid var(--chart-rule); background: #fff;
  color: var(--chart-g1); cursor: pointer; transition: .2s; line-height: 1.2;
}
.chart-chip:hover:not(:disabled) { border-color: var(--chart-g2); color: var(--chart-ink); }
.chart-chip[aria-pressed="true"] { background: var(--chart-ink); border-color: var(--chart-ink); color: #fff; }
.chart-chip .n { opacity: .55; font-size: 12px; margin-left: 5px; }
.chart-search {
  font: inherit; font-size: 14px; flex: 1; min-width: 200px;
  border: 1px solid var(--chart-rule); border-radius: 99px; padding: 8px 16px; background: #fff;
}
.chart-search:focus { outline: none; border-color: var(--chart-ink); }
.chart-count { font-size: 13px; color: var(--chart-g2); margin-left: auto; white-space: nowrap; }

/* ---------- card grid ---------- */
.chart-grid {
  padding: 1px;   /* room for the 1px ring, which paints outside the box */ display: grid; grid-template-columns: repeat(2, 1fr); gap: 14px; }
@media (min-width: 560px) { .chart-grid {
  padding: 1px;   /* room for the 1px ring, which paints outside the box */ grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 900px) { .chart-grid--3 { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 900px) { .chart-grid--4, .chart-grid--5 { grid-template-columns: repeat(4, 1fr); } }
@media (min-width: 1180px) { .chart-grid--5 { grid-template-columns: repeat(5, 1fr); } }

/* No transform on the lift.
   Safari promotes transformed elements to their own compositing layer, and a
   1px border on a promoted layer renders soft — it looked like a white haze
   along the bottom edge of every card. Shadow alone gives the same effect
   without touching the compositing path. */
/* Card edges are drawn with an outer box-shadow ring, not a border.
   These boxes clip their children with border-radius + overflow:hidden. A
   border sits exactly on that clip boundary, and Safari antialiases the two
   against each other — which reads as a pale seam along the bottom edge.
   The debug outline proved it: outlines paint OUTSIDE the border box and were
   crisp all the way round while the border was not. An outer box-shadow paints
   in the same place as an outline, so it is immune to the clip.
   Do not turn these back into borders. */
/* Qualified with .chart-block, and it has to be.
   Elementor's frontend.min.css carries `.elementor a { box-shadow: none }` at
   (0,1,1). A card IS an anchor, and a bare `.chart-card` scores (0,1,0) — so
   the ring was not faint, it was being removed altogether on every Elementor
   page. Darkening --chart-edge could never have fixed that; only out-scoring
   the rule does. Same failure as the headings and the news columns. */
.chart-block .chart-card {
  border: 0;
  border-radius: var(--chart-r);
  overflow: hidden;
  background: #fff;
  display: flex;
  flex-direction: column;
  /* The ring was #D3D9E0 for a long time, which worked while every card was
     topped by a photograph that supplied its own hard edge. Cards with a white
     or letterboxed image area disappeared against a white page. Darker ring
     plus a faint resting shadow, applied to every card so news, breakthroughs
     and projects all sit on the page the same way. */
  box-shadow: 0 0 0 1px var(--chart-edge),
              0 1px 2px rgba(15, 17, 20, .045);
  transition: box-shadow .28s ease;
}
.chart-block .chart-card:hover {
  box-shadow: 0 0 0 1px #A9B2BD,
              0 1px 2px rgba(15, 17, 20, .06),
              0 12px 28px -18px rgba(15, 17, 20, .3);
}
.chart-card figure { margin: 0; aspect-ratio: 16/10; overflow: hidden;
  background: var(--chart-wash); border-bottom: 1px solid var(--chart-edge); }
.chart-card figure img { width: 100%; height: 100%; object-fit: cover; }
.chart-noimg { display: block; width: 100%; height: 100%; background:
  repeating-linear-gradient(45deg, #F2F3F5 0 8px, #EDEFF1 8px 16px); }
.chart-card-body { padding: 15px 15px 16px; display: flex; flex-direction: column; gap: 6px; flex: 1; }
.chart-card-title { font-size: 15.5px; font-weight: 600; line-height: 1.3; letter-spacing: -.015em; }
.chart-card-desc {
  font-size: 12.5px; line-height: 1.45; color: var(--chart-g1);
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
/* The card footer wraps, and the project tag always takes its own line.
   Three items — kind, project, date — do not fit across a 269px card: they
   measure about 274px before gaps even with the shortest project name, so the
   old `flex-wrap: nowrap` had them overlapping on the desktop and colliding
   almost everywhere on a phone.
   Letting them wrap freely gave ragged footers of one, two or three lines
   depending on the name. Giving the project tag `flex-basis: 100%` makes the
   layout deterministic instead: kind and date share the first line, the
   project sits on the second, and a card with no project stays on one line.
   Across the news grid that is 6 cards on one line, 19 on two, and only two
   distinct card heights. */
.chart-card-foot {
  margin-top: auto; padding-top: 13px;
  display: flex; flex-wrap: wrap; align-items: center;
  justify-content: flex-start; gap: 7px 8px;
}
.chart-card-foot .chart-date { margin-left: auto; order: 1; }
.chart-card-foot .chart-tag--proj {
  order: 2; flex-basis: 100%; max-width: 100%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chart-date { font-size: 12px; color: var(--chart-g2); white-space: nowrap; }

/* ---------- tags ---------- */
.chart-tags { display: flex; gap: 5px; flex-wrap: wrap; margin-top: auto; padding-top: 13px; }
.chart-tag {
  font-size: 10.5px; font-weight: 500; padding: 3px 8px; border-radius: 99px;
  background: var(--chart-wash); color: var(--chart-g1);
  border: 1px solid var(--chart-rule); white-space: nowrap;
}
a.chart-tag:hover { border-color: var(--chart-red); color: var(--chart-red); background: var(--chart-red-soft); }
.chart-tag--live { background: var(--chart-red-soft); color: var(--chart-red); border-color: var(--chart-red-line); }
.chart-tag--done { color: var(--chart-g2); }
.chart-tag--proj { font-weight: 600; color: var(--chart-ink); }
.chart-tag--inst { background: var(--chart-red-soft); color: var(--chart-red); border-color: var(--chart-red-line); }

/* ---------- lists ---------- */
/* ---------- publications ----------

   The theme draws its own borders and backgrounds on lists inside post
   content, which boxed every year group and every row. Neither box is ours,
   so they are switched off explicitly here rather than left to chance —
   the layout is a hairline between rows and nothing else.

   Tags sit UNDER the title on their own line. Putting them in the right-hand
   gutter compresses long titles and makes the column ragged; the two-line
   arrangement lets a title run the full width. */

.chart-yeargroup { margin-bottom: 40px; }
.chart-block .chart-yearhead {
  font-size: 20px; font-weight: 600; letter-spacing: -.022em;
  padding: 0 0 11px; margin: 0 0 2px;
  border-bottom: 1px solid var(--chart-rule);
  display: flex; align-items: baseline; gap: 11px;
}
.chart-block .chart-yearcount {
  font-size: 13px; font-weight: 400; letter-spacing: 0; color: var(--chart-g2);
}

.chart-block .chart-list,
.chart-block .chart-list li {
  border: 0; background: none; box-shadow: none; border-radius: 0;
}
.chart-list { list-style: none; margin: 0; padding: 0; }

.chart-block .chart-list li {
  display: block;
  padding: 15px 6px 15px;
  border-bottom: 1px solid var(--chart-rule);
  transition: background .18s;
}
.chart-block .chart-list li:last-child { border-bottom: 0; }
.chart-list li:hover { background: var(--chart-wash); }

.chart-list .t {
  display: block;
  font-size: 15.5px; font-weight: 600; line-height: 1.4;
  letter-spacing: -.012em; color: var(--chart-ink);
  margin-bottom: 9px;
}
.chart-list li:hover .t a { color: var(--chart-red); }
.chart-meta { display: flex; gap: 7px; align-items: center; flex-wrap: wrap; }
.chart-list .pdf {
  font-size: 11.5px; font-weight: 500; color: var(--chart-g2);
  border: 1px solid var(--chart-rule); border-radius: 99px; padding: 2px 9px;
  background: #fff; transition: .2s; white-space: nowrap;
}
.chart-list .pdf:hover { color: var(--chart-red); border-color: var(--chart-red-line); background: var(--chart-red-soft); }

/* ---------- news list ---------- */
.chart-newslist {
  list-style: none; margin: 0; padding: 0;
  border: 0; border-radius: var(--chart-r); overflow: hidden; background: #fff;
  box-shadow: 0 0 0 1px var(--chart-edge);
}
.chart-newslist li { border-bottom: 1px solid var(--chart-rule); }
.chart-newslist li:last-child { border-bottom: 0; }
/* Date on the left, title in the middle, kind on the right.
   The wide-screen rule below used to be written as `.chart-newslist a`, which
   scores 0,1,1 and therefore lost to the 0,2,1 selector above it — so the list
   stayed single-column at every width, and because every child of a one-column
   grid is stretched to the column, the kind tags rendered full width. Both
   selectors now carry .chart-block so the cascade runs in source order. */
.chart-block .chart-newslist a {
  display: grid; grid-template-columns: 1fr; gap: 5px;
  padding: 15px 20px; transition: background .2s; align-items: center;
}
@media (min-width: 720px) {
  .chart-block .chart-newslist a {
    grid-template-columns: 108px 1fr auto;
    gap: 20px;
  }
}
/* A tag must never stretch: it is sized by its text, not by its column. */
.chart-block .chart-newslist .chart-tag { justify-self: start; }
@media (min-width: 720px) {
  .chart-block .chart-newslist .chart-tag { justify-self: end; }
}
.chart-block .chart-newslist .chart-date {
  font-size: 12.5px; color: var(--chart-g2); white-space: nowrap;
}
.chart-newslist a:hover { background: var(--chart-wash); }
.chart-newstitle { font-size: 15.5px; font-weight: 500; letter-spacing: -.012em; line-height: 1.4; }
.chart-newslist a:hover .chart-newstitle { color: var(--chart-red); }

/* ---------- team ---------- */
.chart-board { margin-bottom: 52px; }
.chart-block .chart-boardhead {
  font-size: 26px; font-weight: 700; letter-spacing: -.028em; line-height: 1.2;
  padding: 0 0 14px; margin: 0;
  border-bottom: 1px solid var(--chart-rule);
}
.chart-block .chart-boardblurb {
  font-size: 14.5px; line-height: 1.6; color: var(--chart-g1);
  max-width: 70ch; padding: 0; margin: 18px 0 30px;
}

/* When a board has no description the grid would sit hard against the rule. */
.chart-block .chart-boardhead + .chart-people { margin-top: 24px; }

/* Five across on a desktop. The portraits are roughly 300px originals, so a
   quarter-width tile enlarges them past their own resolution and they go soft;
   a fifth-width tile shows them at about their native size. */
.chart-people { display: grid; grid-template-columns: repeat(2, 1fr); gap: 13px; }
@media (min-width: 560px)  { .chart-people { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 800px)  { .chart-people { grid-template-columns: repeat(4, 1fr); } }
@media (min-width: 1060px) { .chart-people { grid-template-columns: repeat(5, 1fr); } }
.chart-person {
  border: 0; border-radius: var(--chart-r);
  overflow: hidden; background: #fff; display: flex; flex-direction: column;
  box-shadow: 0 0 0 1px var(--chart-edge);
  transition: box-shadow .26s ease;
}
.chart-person:hover { box-shadow: 0 0 0 1px #C9CED4, 0 1px 2px rgba(15,17,20,.05), 0 12px 26px -18px rgba(15,17,20,.24); }
.chart-person figure { margin: 0; aspect-ratio: 1/1; overflow: hidden;
  background: var(--chart-wash); border-bottom: 1px solid var(--chart-edge); }
.chart-person figure img { width: 100%; height: 100%; object-fit: cover; object-position: center top;
  filter: grayscale(1); transition: filter .35s ease; }
.chart-person:hover figure img { filter: none; }
.chart-person-body { padding: 12px 13px 14px; display: flex; flex-direction: column; gap: 4px; flex: 1; }
.chart-role {
  align-self: flex-start; font-size: 10.5px; font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; color: var(--chart-red); background: var(--chart-red-soft);
  border: 1px solid var(--chart-red-line); border-radius: 99px; padding: 2px 7px;
  font-size: 9.5px;
}
.chart-pname { font-size: 13.5px; font-weight: 600; letter-spacing: -.015em; line-height: 1.32; }
.chart-pinst { font-size: 11.5px; color: var(--chart-g2); line-height: 1.35; }
.chart-pdelegate { font-size: 12px; color: var(--chart-g1); }
.chart-pdelegate b {
  display: block; font-size: 10.5px; font-weight: 600; letter-spacing: .05em;
  text-transform: uppercase; color: var(--chart-g2); margin-bottom: 2px;
}

/* ---------- empty state ---------- */
.chart-empty {
  border: 1px dashed var(--chart-rule); border-radius: var(--chart-r);
  padding: 40px 24px; text-align: center; color: var(--chart-g2); font-size: 14px;
}

/* ---------- project page: facts list and overview ---------- */
.chart-facts-list { margin: 0; padding: 0; }
.chart-facts-list dt {
  font-size: 11.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--chart-g2); margin: 0 0 3px;
}
.chart-facts-list dd {
  margin: 0 0 15px; padding: 0 0 15px;
  border-bottom: 1px solid var(--chart-rule);
  font-size: 14.5px; color: var(--chart-ink); line-height: 1.45;
}
.chart-facts-list dd:last-child { border-bottom: 0; margin-bottom: 0; padding-bottom: 0; }
.chart-abstract { font-size: 16.5px; line-height: 1.62; color: var(--chart-g1); margin: 0; }

/* The institute is the tag a reader scans for, so it carries the accent.
   Project and topic stay neutral so the row does not turn into confetti. */
.chart-tag--inst {
  background: var(--chart-red-soft); color: var(--chart-red);
  border-color: var(--chart-red-line);
}

/* Specificity note: `.chart-block h3` (0,1,1) outranks `.chart-yearhead`
   (0,1,0), so the reset's `padding: 0` was winning and the rule sat on the
   text instead of below it. Component rules that style a bare element must
   be qualified with .chart-block to outrank the reset. */

.chart-controls { gap: 10px; align-items: center; }
.chart-select {
  font-family: inherit; font-size: 13.5px; color: var(--chart-ink);
  padding: 8px 30px 8px 13px; border: 1px solid var(--chart-edge);
  border-radius: 99px; background: #fff; cursor: pointer; line-height: 1.2;
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath fill='%239AA0A8' d='M0 0h10L5 6z'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 12px center;
  transition: border-color .2s;
}
.chart-select:hover { border-color: var(--chart-g2); }
.chart-select:focus-visible { outline: 2px solid var(--chart-red); outline-offset: 2px; }
.chart-list[data-flat] { margin-top: 6px; }

/* Themes commonly set `select { width: 100% }` so that form fields fill their
   row, which stretched these two controls across the whole page. The selector
   below scores 0,2,1 and so outranks any bare-element rule, wherever it comes
   from — the same specificity trap that caught the year headings. */
.chart-block select.chart-select {
  width: auto;
  flex: 0 0 auto;
  max-width: 240px;
  text-overflow: ellipsis;
}

/* Portrait placeholder. A flat tint with initials rather than the diagonal
   hatch used for missing project figures — a hatch on a face-shaped slot
   looks like a loading error, initials look like a choice. */
.chart-monogram {
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 100%;
  background: var(--chart-wash);
  color: var(--chart-g2);
  font-size: 24px; font-weight: 600; letter-spacing: .02em;
  user-select: none;
}

/* The team heading was being overlapped by the card grid on the live site,
   which the local preview never reproduced — so the cause is something the
   theme contributes, not this stylesheet. These three rules close off the
   usual suspects: an uncontained float from the theme, a stacking context
   that lets later content paint over the heading, and a grid that starts
   before the heading has finished taking up space.

   If the overlap survives all of this, it is not a flow problem and the
   ?chart_debug=1 outlines will show what is really going on. */
.chart-block .chart-board {
  display: flow-root;      /* contains any float the theme introduces */
  position: relative;
  clear: both;
}
.chart-block .chart-boardhead {
  position: relative;
  z-index: 1;
  clear: both;
  display: block;          /* in case the theme makes headings flex or grid */
  overflow: visible;
}
.chart-block .chart-people { clear: both; }

/* ---------- key-figure strip (homepage) ---------- */
/* The column count is not written down.
   It used to be repeat(3, 1fr), which was right for exactly three figures and
   silently wrapped the fourth onto a row of its own. auto-fit lets the strip
   hold whatever [chart_facts] is asked to show — four across at full width,
   fewer as the screen narrows, one on a phone — without anyone editing CSS
   when a figure is added or dropped.

   The hairlines come from a 1px gap letting the container's background show
   through, rather than a right-hand shadow on every cell but the last. That
   old approach assumed a single row: as soon as the figures wrapped, the cell
   at the end of the first row kept a divider it should not have had, and the
   one below it lost the divider it needed. Gap lines are correct for any
   arrangement, including a partly filled last row. */
.chart-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 1px;
  background: var(--chart-edge);
  border-radius: var(--chart-r); overflow: hidden;
  box-shadow: 0 0 0 1px var(--chart-edge);
}
.chart-facts > * {
  background: var(--chart-wash);
  padding: 14px 20px;
  display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap;
}
.chart-facts b {
  font-size: 21px; font-weight: 700; letter-spacing: -.03em; color: var(--chart-ink);
}
.chart-facts span { font-size: 12.5px; color: var(--chart-g1); }

/* ---------- annual meetings ----------
   A row of three, collapsing to one column on a phone. The year does the
   scanning work — a visitor should see "2025, 2023, 2022" and understand the
   cadence before reading a word. */
.chart-block .chart-meetings {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 12px;
  padding: 1px;   /* room for the ring, which paints outside the box */
}
.chart-block .chart-meeting {
  display: flex; align-items: flex-start; gap: 14px;
  padding: 16px 18px;
  background: #fff;
  border-radius: var(--chart-r);
  box-shadow: 0 0 0 1px var(--chart-edge);
  transition: box-shadow .22s ease;
}
.chart-block a.chart-meeting:hover {
  box-shadow: 0 0 0 1px #C9CED4, 0 12px 26px -18px rgba(15,17,20,.35);
}
.chart-block .chart-meeting-year {
  font-size: 21px; font-weight: 700; letter-spacing: -.03em;
  color: var(--chart-red); line-height: 1.15; flex: none;
}
.chart-block .chart-meeting-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.chart-block .chart-meeting-title { font-size: 14.5px; font-weight: 600; color: var(--chart-ink); }
.chart-block .chart-meeting-sub { font-size: 13px; color: var(--chart-g1); }
.chart-block .chart-meeting-meta { font-size: 12.5px; color: var(--chart-g2); }
.chart-block .chart-meeting .chart-arrow {
  margin-left: auto; color: var(--chart-g2); font-style: normal; flex: none;
}
.chart-block a.chart-meeting:hover .chart-arrow { color: var(--chart-red); }

/* ---------- long prose pages: Impressum, Privacy ----------
   These pages are ordinary Elementor text widgets, not .chart-block output, so
   nothing here reaches them by default — the Impressum was running its
   paragraphs at 1220px, about 150 characters a line, which is unreadable.
   Add the CSS class `chart-prose` to the section and the text takes the same
   68-character measure and heading rhythm as the rest of the site.

   Selectors are qualified with .elementor-widget so they score (0,2,1) and
   beat `.elementor-kit-N h2` at (0,1,1). Without that the kit's heading sizes
   win and only the paragraph width changes — the failure mode that made two
   project-page headings look deliberately different weeks ago. */
.chart-prose .elementor-widget p,
.chart-prose .elementor-widget li,
.chart-prose .elementor-widget h2,
.chart-prose .elementor-widget h3,
.chart-prose .elementor-widget h4 {
  max-width: 68ch;
}
.chart-prose .elementor-widget p,
.chart-prose .elementor-widget li {
  font-size: 16px; line-height: 1.62; color: var(--chart-ink, #15171A);
}
.chart-prose .elementor-widget li { margin-bottom: 6px; }
.chart-prose .elementor-widget h2 {
  font-size: 24px; font-weight: 700; letter-spacing: -.02em;
  line-height: 1.25; margin: 34px 0 8px;
}
.chart-prose .elementor-widget h3 {
  font-size: 19px; font-weight: 600; line-height: 1.3; margin: 26px 0 6px;
}
.chart-prose .elementor-widget a { color: var(--chart-red, #E3000F); }

/* ---------- section headers ---------- */
/* No rule under the heading by default. Separating sections is the job of the
   Elementor section that wraps the block — a background tint or a top border
   there can run full-bleed, which a shortcode inside the content column cannot.
   Add rule="yes" on the shortcode if a heading genuinely needs one. */
.chart-block .chart-sechead {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 20px; flex-wrap: wrap;
  padding: 0; margin: 0 0 22px;
}
.chart-block .chart-sechead--rule {
  padding-bottom: 12px;
  border-bottom: 1px solid var(--chart-rule);
}
.chart-block .chart-sechead h2 {
  font-size: 31px; font-weight: 750; letter-spacing: -.032em; line-height: 1.15;
  margin: 0; padding: 0;
}
/* `.chart-block a` sets `color: inherit` to kill theme link colours. That is
   (0,1,1); this is (0,2,0) and wins on class count, which is what keeps the
   red here. Do not reduce it to a single class. */
/* 500, not 600. The mockup's .link-arrow is 500 and the difference is very
   visible at this size — 600 reads as a shout where 500 reads as an invitation. */
.chart-block a.chart-more {
  font-size: 14px; font-weight: 500; color: var(--chart-red);
  white-space: nowrap; transition: opacity .2s;
}
.chart-block a.chart-more:hover { opacity: .68; }

/* ---------- featured project panel ---------- */
.chart-block .chart-feature {
  display: grid; grid-template-columns: 1fr; gap: 0;
  border: 0; border-radius: var(--chart-r); overflow: hidden; background: #fff;
  /* A resting shadow, not only on hover: this card is the visual anchor of the
     homepage and should sit above the page rather than flat on it. */
  box-shadow: 0 0 0 1px var(--chart-edge),
              0 1px 2px rgba(15,17,20,.04),
              0 18px 40px -24px rgba(15,17,20,.30);
  transition: box-shadow .28s ease, transform .28s ease;
}

.chart-block .chart-feature:hover {
  box-shadow: 0 0 0 1px #C9CED4,
              0 2px 4px rgba(15,17,20,.05),
              0 26px 54px -26px rgba(15,17,20,.36);
}
/* The whole image, not a crop — the same decision as the project cards, and for
   the same reason: most of these are diagrams and plots, and cover silently
   cuts the axes off the top and bottom. White rather than the wash, so a figure
   on a white background reads as one surface with the card. */
.chart-feature figure {
  margin: 0; aspect-ratio: 16/9; overflow: hidden;
  background: #fff; padding: 12px;
}
.chart-feature figure img { width: 100%; height: 100%; object-fit: contain; }
.chart-feature-body {
  padding: 20px 22px 22px; display: flex; flex-direction: column;
  gap: 9px; flex: 1;
}
/* `.chart-block a *` sets `color: inherit` to strip theme link colours, and
   scores (0,1,2). A single class cannot beat that, which is why these two came
   out black inside the card. Naming the parent anchor takes them to (0,2,0).
   Any coloured text placed inside a card anchor needs the same treatment. */
.chart-block .chart-feature .chart-eyebrow {
  font-size: 12px; font-weight: 600; letter-spacing: .1em; text-transform: uppercase;
  color: var(--chart-red);
}
.chart-feature-title {
  font-size: 21px; font-weight: 700; letter-spacing: -.028em; line-height: 1.2;
  color: var(--chart-ink);
}
.chart-block .chart-feature .chart-feature-desc {
  font-size: 14.5px; line-height: 1.6; color: var(--chart-g1);
  display: block; overflow: visible; -webkit-line-clamp: none;
}

/* Reads as a link, is not one — the card itself carries the href. */
.chart-block .chart-feature .chart-readmore {
  margin-top: auto; padding-top: 6px;
  font-size: 14px; font-weight: 500; color: var(--chart-red);
}
.chart-feature:hover .chart-readmore { opacity: .68; }

/* ---------- hero furniture: pill, buttons, arrows ---------- */
.chart-arrow { font-style: normal; margin-left: 6px; transition: transform .2s ease; display: inline-block; }
a:hover > .chart-arrow, a:hover .chart-arrow { transform: translateX(3px); }

.chart-pill {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 15px; border-radius: 99px;
  background: var(--chart-red-soft); border: 1px solid var(--chart-red-line);
  color: var(--chart-red); font-size: 12.5px; font-weight: 600; letter-spacing: -.005em;
}
.chart-pill-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--chart-red); flex: 0 0 auto;
}

.chart-block a.chart-btn,
a.chart-btn {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 13px 24px; border-radius: 8px;
  font-size: 14.5px; font-weight: 500; letter-spacing: -.005em;
  background: var(--chart-red); color: #fff;
  border: 0; box-shadow: 0 0 0 1px var(--chart-red);
  transition: background .2s, box-shadow .2s, color .2s;
}
.chart-block a.chart-btn:hover, a.chart-btn:hover { background: #c40010; box-shadow: 0 0 0 1px #c40010; }
.chart-block a.chart-btn--ghost,
a.chart-btn--ghost {
  background: #fff; color: var(--chart-ink); box-shadow: 0 0 0 1px #D3D9E0;
}
.chart-block a.chart-btn--ghost:hover, a.chart-btn--ghost:hover {
  background: #fff; color: var(--chart-red); box-shadow: 0 0 0 1px #F8D5D8;
}

/* a linked figure keeps the same box as an unlinked one */
.chart-facts a { color: inherit; }
.chart-facts a:hover span { color: var(--chart-red); }
.chart-facts a .chart-arrow { color: var(--chart-red); }

/* An inline wrapper for shortcodes that render a single control rather than a
   block: it carries the custom properties and the link reset without forcing
   a line break the way .chart-block alone would. */
.chart-block.chart-inline {
  display: inline-block;
  font-size: inherit;
  line-height: inherit;
  vertical-align: middle;
}
/* The block rhythm below puts 34px above any .chart-block that follows another
   one. Two buttons side by side are both .chart-block, so the second inherited
   that 34px as a TOP margin and dropped below the first — which is why the
   homepage pair stopped lining up. Inline blocks sit in a line, not in the
   stack, so they opt out of the rhythm explicitly. */
.chart-block.chart-inline + .chart-block.chart-inline {
  margin-top: 0;
  margin-left: 10px;
}

/* On a narrow screen the two buttons wrap, and inline-block wraps badly: the
   line advances by the parent's line-height (32px) rather than the button's
   height (52px), so the second button lands touching or overlapping the first,
   and it keeps its 10px left margin, so it is not aligned with the button
   above it. Both faults are the same cause — laying buttons out as if they
   were words in a sentence.

   Making the shortcode's own wrapper a wrapping flex row fixes both: a real
   gap in each direction, and a shared left edge when stacked. The margin is
   zeroed only inside that container, so a browser without :has() keeps the
   old margin and the old behaviour rather than losing the spacing. */
.elementor-shortcode:has(> .chart-block.chart-inline) {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
}
.elementor-shortcode:has(> .chart-block.chart-inline)
  > .chart-block.chart-inline + .chart-block.chart-inline {
  margin-left: 0;
}

/* Belt and braces: kill the underline on these two wherever they end up, in
   case a theme reaches them through a selector we have not anticipated.

   NOTE: this deliberately does NOT touch border-bottom. An earlier version
   included `border-bottom: 0 !important` here to defeat themes that fake an
   underline with a border — and promptly deleted the buttons' own bottom edge,
   which is the third time that exact mistake has been made in this stylesheet.
   A blanket border reset can never tell a decoration from a design. */
.chart-btn, .chart-btn:hover, .chart-btn:focus,
.chart-pill, .chart-pill * {
  text-decoration: none !important;
  -webkit-text-decoration: none !important;
}
/* Explicit hex, not a variable — these can render outside .chart-block. */
.chart-btn--ghost { box-shadow: 0 0 0 1px #D3D9E0; }

/* ---------- roadmap ----------
   One CSS grid shared by the axis and every row, so bars line up with their
   years without any measurement in JavaScript. The label column is fixed; the
   years divide what is left. Narrow screens scroll horizontally rather than
   squeezing eleven years into 320px, which would make the chart unreadable. */
.chart-roadmap {
  --chart-label: 190px;
  margin-top: 4px;
  overflow-x: auto;
  padding-bottom: 4px;
}
.chart-rm-head,
.chart-rm-row {
  display: grid;
  grid-template-columns: var(--chart-label) repeat(var(--chart-span), minmax(46px, 1fr));
  align-items: center;
  min-width: 640px;
}
.chart-rm-head {
  position: sticky; top: 0; z-index: 2;
  background: #fff;
  padding-bottom: 7px;
  margin-bottom: 4px;
  box-shadow: 0 1px 0 var(--chart-rule);
}
.chart-rm-year {
  font-size: 11.5px; font-weight: 500; color: var(--chart-g2);
  text-align: center; letter-spacing: .02em;
}
.chart-rm-year.is-now { color: var(--chart-red); font-weight: 600; }

.chart-rm-row { padding: 3px 0; border-radius: 6px; transition: background .18s; }
.chart-rm-row:hover { background: var(--chart-wash); }

.chart-block a.chart-rm-label {
  grid-column: 1;
  padding-right: 14px;
  font-size: 13.5px; font-weight: 500; line-height: 1.3;
  color: var(--chart-ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chart-rm-row:hover .chart-rm-label { color: var(--chart-red); }

.chart-rm-bar {
  height: 26px; border-radius: 5px;
  display: flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 500; letter-spacing: .01em;
  background: var(--chart-wash); color: var(--chart-g2);
  box-shadow: inset 0 0 0 1px var(--chart-edge);
  white-space: nowrap; overflow: hidden;
}
.chart-rm-bar.is-current {
  background: var(--chart-red-soft); color: var(--chart-red);
  box-shadow: inset 0 0 0 1px var(--chart-red-line);
}
@media (max-width: 720px) {
  .chart-roadmap { --chart-label: 140px; }
  .chart-rm-bar { font-size: 0; }   /* the bar itself carries the meaning */
}

/* ---------- document download card ---------- */
.chart-block a.chart-doc {
  display: flex; align-items: center; gap: 18px;
  padding: 18px 20px;
  border: 0; border-radius: var(--chart-r); background: #fff;
  box-shadow: 0 0 0 1px var(--chart-edge);
  transition: box-shadow .24s ease;
}
.chart-block a.chart-doc:hover {
  box-shadow: 0 0 0 1px #C9CED4,
              0 1px 2px rgba(15,17,20,.05),
              0 14px 30px -20px rgba(15,17,20,.26);
}
.chart-doc-icon {
  flex: 0 0 auto; width: 40px; height: 52px; border-radius: 5px;
  display: flex; align-items: center; justify-content: center;
  background: var(--chart-red-soft); color: var(--chart-red);
  font-size: 11px; font-weight: 700; letter-spacing: .06em;
  box-shadow: inset 0 0 0 1px var(--chart-red-line);
}
.chart-doc-cover {
  flex: 0 0 auto; width: 40px; height: 52px; border-radius: 3px; overflow: hidden;
  box-shadow: 0 0 0 1px var(--chart-edge);
}
.chart-doc-cover img { width: 100%; height: 100%; object-fit: cover; }
.chart-doc-body { display: flex; flex-direction: column; gap: 3px; flex: 1; min-width: 0; }
.chart-doc-title {
  font-size: 16px; font-weight: 600; letter-spacing: -.015em; color: var(--chart-ink);
}
.chart-doc-note { font-size: 13.5px; color: var(--chart-g1); }
.chart-doc-meta {
  font-size: 12px; color: var(--chart-g2); letter-spacing: .02em;
}
.chart-doc-cta {
  flex: 0 0 auto; font-size: 14px; font-weight: 500; color: var(--chart-red);
  white-space: nowrap;
}
.chart-doc:hover .chart-doc-cta .chart-arrow { transform: translateY(2px); }
@media (max-width: 560px) {
  .chart-block a.chart-doc { flex-wrap: wrap; }
  .chart-doc-cta { width: 100%; }
}

/* ---------- document viewer ---------- */
.chart-viewer[hidden] { display: none; }
.chart-viewer {
  position: fixed; inset: 0; z-index: 99999;
  background: rgba(15,17,20,.72);
  display: flex; flex-direction: column;
  padding: 3vh 3vw;
}
.chart-viewer-bar {
  display: flex; align-items: center; gap: 18px;
  padding: 0 2px 12px;
  color: #fff;
}
.chart-viewer-title {
  font-size: 15px; font-weight: 600; margin-right: auto;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chart-viewer .chart-viewer-open {
  font-size: 13.5px; font-weight: 500; color: #fff; opacity: .8;
  white-space: nowrap; text-decoration: none;
}
.chart-viewer .chart-viewer-open:hover { opacity: 1; }
.chart-viewer-close {
  background: none; border: 0; color: #fff; font-size: 26px; line-height: 1;
  cursor: pointer; padding: 0 4px; opacity: .8;
}
.chart-viewer-close:hover { opacity: 1; }
.chart-viewer iframe {
  flex: 1; width: 100%; border: 0; border-radius: 6px; background: #fff;
}

/* ---------- compact list rows ----------
   Publications keep title and tags on separate lines: their titles are long
   sentences and the full column width is worth having. Report names are short
   — "MagDev2 / MagDev3", "FCCee HTS4" — so a stacked layout doubles the height
   of an 80-row page for no gain. Tags sit on the right of the same line. */
.chart-block .chart-list--inline li {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 18px; flex-wrap: wrap;
  padding: 11px 6px;
}
.chart-list--inline .t {
  flex: 1; min-width: 220px; margin-bottom: 0;
  font-size: 15px; font-weight: 500;
}
.chart-list--inline .chart-meta { flex: 0 0 auto; }

/* ---------- header: Contact as a button ----------
   Contact sits in the WordPress menu like every other page, so there is one
   menu to maintain and the mobile hamburger picks it up for free. On desktop
   this rule dresses that one item as the dark pill from the mockup.

   To switch it on: Design → Menüs → Ansicht anpassen → tick "CSS-Klassen",
   then put  chart-contact  in the Contact item's CSS Classes field.

   Scoped to .elementor-nav-menu--main deliberately. The mobile hamburger uses
   .elementor-nav-menu--dropdown, which this never touches, so inside the
   dropdown Contact stays an ordinary row rather than a full-width black slab.
   That was the whole reason for moving it out of a Button widget.

   !important throughout because Elementor writes its own menu colours and
   padding into a per-widget stylesheet whose specificity varies by version.
   One menu item is a small enough blast radius to be worth the certainty. */
.elementor-nav-menu--main .elementor-nav-menu > li.chart-contact > a.elementor-item {
  background-color: #15171A !important;
  color: #FFFFFF !important;
  border-radius: 7px !important;
  padding: 10px 18px !important;
  margin-left: 10px;
  font-size: 13.5px;
  font-weight: 500;
  line-height: 1.2;
  transition: background-color .22s ease;
}
.elementor-nav-menu--main .elementor-nav-menu > li.chart-contact > a.elementor-item:hover,
.elementor-nav-menu--main .elementor-nav-menu > li.chart-contact > a.elementor-item:focus {
  background-color: #E3000F !important;
  color: #FFFFFF !important;
}

/* Red is the hover state and nothing else. Elementor marks the current page's
   menu item with .elementor-item-active, and an earlier version of this rule
   grouped that selector with :hover — so the button sat permanently red on the
   Contact page itself. A button is an invitation to act, not a location
   marker; the other menu items already show where you are. */
.elementor-nav-menu--main .elementor-nav-menu > li.chart-contact > a.elementor-item.elementor-item-active {
  background-color: #15171A !important;
  color: #FFFFFF !important;
}

/* The pointer effect draws a grey pillbox in a pseudo-element behind the item.
   Behind a dark background it shows as a halo on the corners, so switch it off
   for this one item only — every other menu item keeps its pillbox. */
.elementor-nav-menu--main .elementor-nav-menu > li.chart-contact > a.elementor-item:before,
.elementor-nav-menu--main .elementor-nav-menu > li.chart-contact > a.elementor-item:after {
  display: none !important;
}

/* ---------- footer text ----------
   The Text-Editor widget has no link-colour control, so footer links come out
   browser-default blue. Setting a global link colour in Site Settings would
   fix the footer and repaint every link on the site, so it is done here with
   two opt-in classes instead.

   Add via the widget's Advanced → CSS Classes field:
     chart-foot       the four footer columns
     chart-footnote   the copyright line at the bottom

   !important on colour only: OceanWP sets link colour from several selectors
   and which one wins varies by page template. */
.chart-foot,
.chart-foot p,
.chart-foot li {
  font-size: 14px;
  line-height: 1.9;
  color: #5C6169;
}
.chart-foot a,
.chart-foot a:link,
.chart-foot a:visited {
  color: #5C6169 !important;
  text-decoration: none;
  border-bottom: 0;
}
.chart-foot a:hover,
.chart-foot a:focus {
  color: #E3000F !important;
}

.chart-footnote,
.chart-footnote p {
  font-size: 13px;
  line-height: 1.7;
  color: #9AA0A8;
}
.chart-footnote a,
.chart-footnote a:link,
.chart-footnote a:visited {
  color: #9AA0A8 !important;
  text-decoration: none;
  border-bottom: 0;
}
.chart-footnote a:hover,
.chart-footnote a:focus {
  color: #E3000F !important;
}

/* ---------- partner institute strip ----------
   [chart_partners] renders the logos and nothing else — no heading, no frame,
   no vertical padding. Whatever section it sits in owns the spacing, so the
   same shortcode works under the homepage hero and inside the About page.

   Two decisions worth stating:

   Monochrome by default. Five corporate palettes side by side is what makes a
   logo wall shout; one ink lets the row read as a single texture. Real colour
   returns on hover, so nothing is hidden.

   Optical heights per institute, not one uniform height. A round mark and a
   wordmark of the same pixel height do not carry the same visual weight —
   set them equal and CERN's roundel looks half the size of the others. */
.chart-partners {
  --chart-partner-gap: 34px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--chart-partner-gap);
}
.chart-block .chart-partners a.chart-partner-link,
.chart-partners .chart-partner {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 1 1 0;
  min-width: 110px;
  padding: 6px 4px;
  border-radius: 8px;
  text-decoration: none;
}
.chart-block .chart-partners a.chart-partner-link { padding: 0; }
.chart-partners img {
  display: block;
  width: auto;
  max-width: 100%;
  height: 32px;                 /* default for any institute added later */
}

/* Tuned against the actual artwork. Adjust here, not in the template. */
.chart-partners .chart-partner--cern  img { height: 46px; }
.chart-partners .chart-partner--psi   img { height: 34px; }
.chart-partners .chart-partner--epfl  img { height: 29px; }
.chart-partners .chart-partner--ethz  img { height: 25px; }
.chart-partners .chart-partner--unige img { height: 29px; }

.chart-partners--mono img {
  filter: grayscale(1) opacity(.62);
  transition: filter .25s ease;
}
.chart-partners--mono a.chart-partner-link:hover img,
.chart-partners--mono a.chart-partner-link:focus img {
  filter: grayscale(0) opacity(1);
}

@media (max-width: 820px) {
  .chart-partners { justify-content: center; gap: 26px; }
  .chart-block .chart-partners a.chart-partner-link,
  .chart-partners .chart-partner { flex: 0 1 38%; }
}

/* ---------- project page ---------- */
.chart-pfacts {
  display: flex; flex-wrap: wrap; gap: 0;
  border: 1px solid var(--chart-rule); border-radius: var(--chart-r);
  overflow: hidden;
}
.chart-pfacts .chart-fact {
  flex: 1 1 200px; padding: 15px 20px;
  border-left: 1px solid var(--chart-rule);
}
.chart-pfacts .chart-fact:first-child { border-left: 0; }
.chart-pfacts .chart-fact span {
  display: block; font-size: 11.5px; font-weight: 600; letter-spacing: .07em;
  text-transform: uppercase; color: var(--chart-g2); margin-bottom: 4px;
}
.chart-pfacts .chart-fact b { font-size: 15.5px; font-weight: 600; letter-spacing: -.01em; }
@media (max-width: 640px) {
  .chart-pfacts .chart-fact { flex: 1 1 100%; border-left: 0; border-top: 1px solid var(--chart-rule); }
  .chart-pfacts .chart-fact:first-child { border-top: 0; }
}

.chart-panel-head {
  font-size: 12px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
  color: var(--chart-g2); margin-bottom: 12px;
}
.chart-block .chart-team-list { display: flex; flex-direction: column; gap: 13px; }
.chart-block .chart-team-list li { display: flex; align-items: center; gap: 11px; }
.chart-team-list img, .chart-team-list .chart-initials {
  width: 38px; height: 38px; border-radius: 50%; object-fit: cover; flex: 0 0 38px;
  background: var(--chart-wash); box-shadow: 0 0 0 1px var(--chart-rule);
}
.chart-team-list .chart-initials {
  display: flex; align-items: center; justify-content: center;
  font-size: 12.5px; font-weight: 600; color: var(--chart-g2); text-transform: uppercase;
}
.chart-team-body { display: flex; flex-direction: column; line-height: 1.35; }
.chart-team-name { font-size: 14.5px; font-weight: 600; }
.chart-team-role { font-size: 12px; color: var(--chart-red); font-weight: 500; }
.chart-team-org  { font-size: 12.5px; color: var(--chart-g2); }

/* Milestone strip. Deliberately not a Gantt chart — no grid, no dependencies,
   no deliverable text. It answers "roughly when, and how far through". */
.chart-ms { display: flex; flex-direction: column; gap: 7px; }
.chart-ms-head {
  font-size: 12px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
  color: var(--chart-g2); margin-bottom: 4px;
}
.chart-ms-years {
  display: flex; justify-content: space-between;
  font-size: 11.5px; color: var(--chart-g2); padding: 0 0 2px;
}
.chart-ms-row { display: flex; align-items: center; gap: 12px; }
.chart-ms-label {
  flex: 0 0 40%; font-size: 13.5px; color: var(--chart-g1);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chart-ms-track {
  position: relative; flex: 1; height: 8px; border-radius: 99px;
  background: var(--chart-wash); box-shadow: inset 0 0 0 1px var(--chart-rule);
}
.chart-ms-bar {
  position: absolute; top: 0; height: 8px; border-radius: 99px;
  background: var(--chart-red);
}
.chart-ms-bar.is-done { background: var(--chart-g2); }
.chart-ms-when { flex: 0 0 auto; font-size: 12px; color: var(--chart-g2); }
@media (max-width: 640px) { .chart-ms-label { flex: 0 0 46%; font-size: 12.5px; } }

.chart-block .chart-pnav {
  display: flex; justify-content: space-between; gap: 18px;
  border-top: 1px solid var(--chart-rule); padding-top: 22px; margin-top: 8px;
}
.chart-block .chart-pnav a { display: flex; flex-direction: column; gap: 3px; max-width: 46%; }
.chart-pnav-next { text-align: right; margin-left: auto; }
.chart-pnav span { font-size: 11.5px; font-weight: 600; letter-spacing: .07em;
  text-transform: uppercase; color: var(--chart-g2); }
.chart-pnav b { font-size: 15px; font-weight: 600; letter-spacing: -.01em; }
.chart-block .chart-pnav a:hover b { color: var(--chart-red); }

.chart-block a.chart-extlink {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 14px; font-weight: 500; color: var(--chart-red);
}
.chart-block .chart-partnerlist li {
  font-size: 14px; color: var(--chart-g1); padding: 6px 0;
  border-top: 1px solid var(--chart-rule);
}
.chart-block .chart-partnerlist li:first-child { border-top: 0; }

/* The proposal's formal title, under the project name. Not inside .chart-block
   when dropped in bare, so it is styled on its own class. */
.chart-fulltitle {
  font-size: 17px;
  line-height: 1.5;
  color: #5C6169;
  max-width: 68ch;
  margin: 0 0 6px;
}

/* ---------- roadmap: expandable milestones ----------
   The sub-rows sit inside .chart-rm-row and span every column, so a filter
   that hides the project hides its milestones too. They repeat the parent's
   grid template, which is what keeps the bars aligned to the same year axis. */
.chart-rm-labelcell {
  grid-column: 1;
  display: flex; align-items: center; gap: 2px;
  padding-right: 14px; min-width: 0;
}
.chart-block a.chart-rm-label { grid-column: auto; padding-right: 0; min-width: 0; }

.chart-rm-toggle {
  flex: 0 0 18px; width: 18px; height: 18px;
  border: 0; background: none; padding: 0; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.chart-rm-toggle.is-empty { cursor: default; }
.chart-rm-toggle span {
  display: block; width: 0; height: 0;
  border-left: 4.5px solid var(--chart-g2);
  border-top: 3.5px solid transparent;
  border-bottom: 3.5px solid transparent;
  transition: transform .18s ease;
}
.chart-rm-toggle:hover span { border-left-color: var(--chart-red); }
.chart-rm-toggle[aria-expanded="true"] span { transform: rotate(90deg); }

.chart-rm-subs {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: var(--chart-label) repeat(var(--chart-span), minmax(46px, 1fr));
  gap: 3px 0;
  padding: 5px 0 8px;
}
.chart-rm-sub {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: var(--chart-label) repeat(var(--chart-span), minmax(46px, 1fr));
  align-items: center;
}
.chart-rm-sublabel {
  grid-column: 1;
  padding-left: 22px; padding-right: 14px;
  font-size: 12px; line-height: 1.35; color: var(--chart-g2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chart-rm-subbar {
  height: 8px; border-radius: 99px;
  background: var(--chart-red-soft);
  box-shadow: inset 0 0 0 1px var(--chart-red-line);
}
.chart-rm-subbar.is-done {
  background: var(--chart-wash);
  box-shadow: inset 0 0 0 1px var(--chart-edge);
}
.chart-rm-row:has(.chart-rm-subs:not([hidden])) { background: var(--chart-wash); }
@media (max-width: 720px) { .chart-rm-sublabel { padding-left: 18px; font-size: 11.5px; } }

/* ---------- partner groups on a project page ---------- */
.chart-partners-wrap { display: flex; flex-wrap: wrap; gap: 26px 46px; }
.chart-partnergroup { min-width: 190px; }
.chart-partnerlabel {
  display: block; font-size: 11px; font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase; color: var(--chart-g2); margin-bottom: 7px;
}
.chart-block .chart-partnerlist li {
  font-size: 14.5px; color: var(--chart-ink); padding: 5px 0;
  border-top: 1px solid var(--chart-rule);
}
.chart-block .chart-partnerlist li:first-child { border-top: 0; }

/* ---------- sticky header legibility ----------
   Elementor's sticky header keeps whatever background the section has, and a
   translucent one lets body text show through the menu while scrolling. A near
   opaque white with a blur behind it keeps the glass effect from the mockup
   without making the menu hard to read.

   Scoped to the Theme Builder header location so it cannot touch anything else. */
.elementor-location-header .elementor-sticky--effects,
.elementor-location-header.elementor-sticky--effects,
.elementor-location-header .elementor-section.elementor-sticky--effects {
  background-color: rgba(255, 255, 255, .94) !important;
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  backdrop-filter: saturate(180%) blur(14px);
  box-shadow: 0 1px 0 var(--chart-rule, #E6E8EB);
}
/* Browsers without backdrop-filter get a fully opaque bar rather than a
   translucent one they cannot blur. */
@supports not (backdrop-filter: blur(1px)) {
  .elementor-location-header .elementor-sticky--effects,
  .elementor-location-header.elementor-sticky--effects,
  .elementor-location-header .elementor-section.elementor-sticky--effects {
    background-color: #fff !important;
  }
}

/* ---------- project cards: show the whole image ----------
   Project images are mostly figures — plots, schematics, a few photographs —
   and a centre crop cuts axes and labels off the wide ones. FCCee Lumi lost
   65% of its width at square, 44% at 16:10. So these cards fit the image
   inside the frame instead of filling it, and accept white margins.

   Scoped by [data-topic], which only project cards carry. News and
   breakthrough cards use [data-kind] and keep the crop — those are
   photographs, where filling the frame is right and letterboxing looks weak.

   4:3 rather than square: the selection averages 1.35:1, so a 4:3 frame
   leaves the least empty space. Square would put white bars down both sides
   of nineteen of the twenty-nine. */
.chart-card[data-topic] figure {
  aspect-ratio: 4 / 3;
  background: #fff;
  padding: 10px;
}
.chart-card[data-topic] figure img {
  object-fit: contain;
  width: 100%;
  height: 100%;
}
/* The placeholder still fills its frame — there is nothing to letterbox. */
.chart-card[data-topic] figure .chart-noimg { margin: -10px; width: auto; }

/* The card edge is set once on .chart-card, from --chart-edge, for every card
   type. The project-only override that used to live here became identical and
   was removed rather than left to drift. */

/* ---------- headings: two levels, deliberately ----------
   Elementor's Site Settings write `.elementor-kit-7 h2` and `h3` — specificity
   (0,1,1). Bare component classes like `.chart-ms-head` score (0,1,0) and lose,
   so those headings were rendering as theme h3s with only `text-transform`
   surviving. That is why exactly two headings on a project page were uppercase
   and the rest were not: an accident, not a decision.

   Everything below is qualified with `.chart-block`, which scores (0,2,0) and
   beats the kit on class count. Same trap as the card frames and the news
   columns; the fix is the same.

   Level 1 — .chart-secthead: a section of the main column.
   Level 2 — .chart-panel-head: a label on a sidebar panel. */
.chart-block .chart-secthead,
.chart-block h2.chart-secthead,
.chart-block h3.chart-secthead {
  font-size: 19px;
  font-weight: 600;
  letter-spacing: .045em;
  text-transform: uppercase;
  line-height: 1.25;
  color: var(--chart-ink);
  margin: 0 0 16px;
  padding: 0;
}

/* The section head used by the list blocks carries a "See all" link on the
   right, so its heading has to match the standalone one exactly. */
.chart-block .chart-sechead h2 {
  font-size: 19px;
  font-weight: 600;
  letter-spacing: .045em;
  text-transform: uppercase;
  line-height: 1.25;
  color: var(--chart-ink);
  margin: 0;
  padding: 0;
}

.chart-block .chart-panel-head,
.chart-block h3.chart-panel-head {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  line-height: 1.3;
  color: var(--chart-g2);
  margin: 0 0 12px;
  padding: 0;
}

.chart-block .chart-ms-head { /* superseded by .chart-secthead, kept for safety */
  font-size: 19px; font-weight: 600; letter-spacing: .045em;
  text-transform: uppercase; color: var(--chart-ink); margin: 0 0 16px;
}

/* One vertical rhythm between stacked blocks, so the gaps stop varying with
   whatever margin each Elementor widget happens to carry. */
.chart-block + .chart-block { margin-top: 34px; }

/* ---------- news article ----------
   Prose, so a single measured column rather than the project page's two.
   The furniture sits above and below the text, never beside it. */
.chart-postmeta {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
  margin-bottom: 4px;
}
.chart-postdate { font-size: 13px; color: var(--chart-g2); }

/* ---------- the article's featured image ----------
   Exactly the width of the prose, on the same left edge.

   The trade-off, stated plainly: 22 of the 23 news images are 350px wide,
   because the old site stored thumbnails and the filenames still end in -350.
   At the text measure they are drawn at about 1.96x, so they are softer than
   the original. That is a deliberate choice — a small sharp picture read as a
   mistake, and the intermediate attempts (a wide grey band around a small
   photo) read worse. Uploading a wider original is the real fix, and needs no
   change here: the rule caps at the measure, it does not force enlargement.

   Two details that took measuring rather than guessing:

   1. The width is 68ch, matching the prose, but `ch` resolves against the
      element's own font. This container sits outside the .single-post content
      rule, so it inherited OceanWP's 18px Open Sans and 68ch came out 5px
      short. Naming the font here makes both measures identical.

   2. Elementor gives every column inside the post body a default horizontal
      padding, so the text was inset 20px and the image was not. Removing that
      inset — rather than nudging the image by a fixed 20px — keeps the two
      aligned on posts built any which way, including future ones written in
      the block editor with no columns at all. */
.single-post .elementor-widget-theme-post-content .elementor-column,
.single-post .elementor-widget-theme-post-content .elementor-widget-wrap {
  padding-left: 0 !important;
  padding-right: 0 !important;
}
.single-post .elementor-widget-theme-post-featured-image .elementor-widget-container {
  background: none;
  padding: 0;
  display: block;
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  font-size: 16px;
  max-width: 68ch;
}
.single-post .elementor-widget-theme-post-featured-image img {
  width: 100%; height: auto;
  border-radius: var(--chart-r, 10px);
  box-shadow: 0 0 0 1px var(--chart-edge, #C3CAD2);
  display: block;
  margin: 0;
}

/* An empty section in the article template contributes nothing but its own
   padding. There is one sitting between the featured image and the text —
   57px tall, two empty columns, 24px above and 32px below — which is most of
   why the gap under the photograph measured 95px rather than 38px.

   Hiding it here rather than asking for it to be deleted, because an empty
   section is dead weight wherever it appears and this cannot remove anything
   real: the selector requires that the section contain no widget at all. */
.single-post .elementor-location-single > .elementor-section:not(:has(.elementor-widget)) {
  display: none;
}

/* Matches the Heading widget the template used to carry, so swapping in
   [chart_post_title] changes nothing visible except that the headline is now
   the article's own. */
.chart-block .chart-posttitle {
  font-size: 38px; font-weight: 700; letter-spacing: -.03em;
  line-height: 1.16; margin: 2px 0 0;
}
@media (max-width: 700px) {
  .chart-block .chart-posttitle { font-size: 29px; }
}
.chart-block a.chart-tag--inst:hover { color: var(--chart-red); }

/* The article body comes from the Elementor Post Content widget, so it is not
   inside .chart-block and needs its own measure. 68ch is about 12 words a
   line, which is where long-form reading stops being work. */
.chart-article .elementor-widget-theme-post-content,
.chart-article p,
.chart-article ul,
.chart-article ol,
.single-post .elementor-widget-theme-post-content p,
.single-post .elementor-widget-theme-post-content ul,
.single-post .elementor-widget-theme-post-content ol {
  max-width: 68ch;
}
.chart-article img,
.single-post .elementor-widget-theme-post-content img {
  border-radius: var(--chart-r, 10px);
}

/* ---------- old articles built as two-column layouts ----------
   Many news posts were written in Elementor with the photograph in a left
   column and the text in a right one. The Post Content widget renders that
   faithfully, so the article arrives already half-width — and removing the
   duplicated image emptied the column without collapsing it.

   Fixing it per post would mean editing 48 articles. Instead every column
   inside an article is forced to full width and the container stacks them, so
   a side-by-side layout becomes image above, text below. For a news item that
   is the right reading order anyway; it is only a loss for a deliberate
   two-up comparison, and there are none in this archive.

   SCOPE. The earlier version of this hung on .chart-article, a CSS class typed
   into the template by hand. That was a mistake: the class ended up on a
   different section from the Post Content widget, so the rules matched nothing
   and the columns stayed at 33/66. A fix that depends on a class being in the
   right place is a fix that silently does nothing when it is not.

   So the scope is now the Post Content widget itself. That element exists
   wherever an article body is rendered, cannot be misplaced, and only ever
   holds the post's own content — nothing in the surrounding template. The
   .chart-article rules stay for the measure and are harmless. */
.chart-article .elementor-container,
.chart-article .elementor-row,
.elementor-widget-theme-post-content .elementor-container,
.elementor-widget-theme-post-content .elementor-row {
  display: block;
}
.chart-article .elementor-column,
.elementor-widget-theme-post-content .elementor-column {
  width: 100% !important;
  max-width: 100% !important;
  flex: none !important;
}
.chart-article .elementor-widget-wrap,
.elementor-widget-theme-post-content .elementor-widget-wrap { width: 100%; }

/* A column left holding nothing — the image was the only thing in it — should
   not contribute its padding to the flow. */
.chart-article .elementor-column > .elementor-widget-wrap:not(:has(*)),
.elementor-widget-theme-post-content .elementor-column > .elementor-widget-wrap:not(:has(*)) {
  display: none;
}
