/* ═══════════════════════════════════════════════════════════════════════════════════════════════
   THE BRAND TIMELINE — styles
   C:\Users\Rossi\Desktop\tracketing-dashboard\assets\css\timeline.css

   🔴 NO COLOUR VALUE LIVES IN THIS FILE. Every colour is a var() of the board's own palette
   (assets/css/board-tokens.css). tools/validate.js fails the build on any hex, rgb() or hsl() here.
   The brief made the Tracketing theme a hard requirement; the prototype's paper-and-amber palette
   was layout only.

   THE ROLE MAP, one place to change a role (the Gate 0 table in timeline/TIMELINE_BUILD_REPORT.md):
   lanes are numbered, not named, so a third brand later is a third variable, not a rewrite.
   The phone's card sheet lives outside .tl-app (a board panel would draw over it otherwise), in
   .tl-portal, so the roles are defined on both.
   ═══════════════════════════════════════════════════════════════════════════════════════════════ */
.tl-app, .tl-portal, .tl-call, .tl-tip {
  --tl-canvas:   var(--bg-primary);
  --tl-card:     var(--bg-card);
  --tl-rule:     var(--border);
  --tl-rule2:    var(--border2);
  --tl-ink:      var(--text-primary);
  --tl-ink2:     var(--text-secondary);
  --tl-ink3:     var(--text-muted);
  /* Two brand colours that differ in LIGHTNESS, not only hue: near-black ink (about 16:1 on the
     page) and the board's dark amber (3.8:1 on the page, over the 3:1 floor for lines and dots).
     Amber is too light for small text (4.5:1), so brand NAMES are always printed in ink, with a
     swatch beside them; the amber only ever draws lines, dots, borders and swatches. Lane position
     and the printed name carry the brand as well, so nothing depends on seeing the colour. */
  --tl-lane-0:   var(--text-primary);
  --tl-lane-1:   var(--amber);
  --tl-axis:     var(--text-primary);
  --tl-today:    var(--text-secondary);
  --tl-future:   var(--chart-track);
  --tl-hatch:    var(--border2);
  --tl-tag:      var(--surface2);
  --tl-media:    var(--surface2);
  /* the main buttons ("All moments" once chosen, "Play the story", the players' play, Sign in) and the words on the
     other filters and the zoom: the Tracketing dark blue, with its yellow on the filled ones (Rossi, 29 Sep) */
  --tl-btn-bg:   var(--navy);
  --tl-btn-fg:   var(--gold);
  --tl-ctl:      var(--navy);
  /* the AI analyst's picture: the board's own top-left logo on white, wherever the call is (Rossi, 29 Sep) */
  --tl-face-bg:  var(--bg-card);
  --tl-focus:    var(--accent);
  --tl-pick-bg:  var(--blue-soft);
  --tl-aivis:      var(--ai-vis);        /* the Weekly AI Visibility row (15 Oct) */
  --tl-aivis-soft: var(--ai-vis-soft);
  --tl-on-aivis:   var(--on-accent);
  /* the story's own colour (its progress bar, its place on the line): the board's yellow, as on Play the story (Rossi,
     30 Sep: "the play the story green progressive bar, change the color from green to the tracketing yellow") */
  --tl-play:     var(--gold);
  --tl-prog-track: var(--navy);
  --tl-up:       var(--positive);
  --tl-down:     var(--negative);
  --tl-shadow:   var(--shadow);
  /* the call (Gate C): an open line is the board's green, hanging up its red, both with white on them */
  --tl-live:     var(--accent);
  --tl-on-live:  var(--on-accent);
  --tl-hang:     var(--negative);
  --tl-on-hang:  var(--on-accent);
  /* the phone that starts a call (the card's button, the one in the panel's words): a call green (Rossi, 30 Sep) */
  --tl-call-green: var(--call-green);
  --tl-alert:    var(--negative);
  /* an ad's label in its network's colour, white words (Rossi, 1 Oct) */
  --tl-ad-meta:     var(--net-meta);
  --tl-ad-google:   var(--net-google);
  --tl-ad-linkedin: var(--net-linkedin);
  --tl-on-ad:       var(--on-accent);
  --tl-ad-headline: var(--net-google-ink);   /* a Google ad's headline, in the blue Google itself prints it in */
  /* a post's picture shown large: on the board's dark blue, white words, its light blue link (Rossi, 1 Oct) */
  --tl-pv-bg:       var(--navy);
  --tl-on-pv:       var(--on-navy);
  --tl-on-pv-2:     var(--on-navy-2);
  --tl-pv-link:     var(--navy-link);
  /* an instant card's little chart in the list (Rossi, 12 Oct: "the card should show a part of the chart"): the card
     frame's own colours (tools/timeline_card_frame_build.js THEME, and ADL's for a third brand on), so the preview
     reads as a piece of the card it opens, in every theme */
  --tl-th-bg:     var(--card-ink-bg);
  --tl-th-ink:    var(--card-text);
  --tl-th-ink2:   var(--card-text-2);
  --tl-th-track:  var(--card-track);
  --tl-th-lav:    var(--on-navy);
  --tl-th-mint:   var(--gold);
  --tl-th-cyan:   var(--card-cyan);
  --tl-th-rose:   var(--card-rose);
  --tl-th-violet: var(--card-violet);
  --tl-th-blue:   var(--card-blue);
  --tl-th-base:   var(--card-bar);
}
.tl-app {
  position: relative;
  height: 100%;
  display: flex;
  flex-direction: column;
  background: var(--tl-canvas);
  color: var(--tl-ink);
  font-family: 'DM Sans', sans-serif;
  font-size: 14px;
  -webkit-font-smoothing: antialiased;
}
.tl-app *, .tl-app *::before, .tl-app *::after { box-sizing: border-box; }
.tl-app [hidden] { display: none !important; }
/* The reset sits at ZERO specificity (:where), so any button's own rule wins. Written as
   `.tl-app button` it outranked `.tl-playbtn` and painted "Play the story" ink on ink. */
:where(.tl-app) button { font: inherit; color: inherit; cursor: pointer; }
.tl-app button:focus-visible, .tl-app a:focus-visible, .tl-app select:focus-visible {
  outline: 2px solid var(--tl-focus); outline-offset: 2px;
}
.tl-sr { position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* ── header ─────────────────────────────────────────────────────────────────────────────────── */
/* Less space above the header and more below it (Rossi, 29 Sep): the follower counter is the tallest item,
   so the bottom padding is all that parts it from the controls, and at 8px it read as one crowded block.
   The gap between groups (header, then controls) is now larger than any gap inside the header.
   Asked again the same evening on the live page ("reduce the gap where I pointed the red arrow by increasing
   the gap where I pointed the blue arrow"): 12/18 became 6/26, so the counter sits near the top edge and the
   controls stand clearly apart from it. */
.tl-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 24px; padding: 6px 24px 26px; flex-wrap: wrap; }
.tl-title { margin: 0; font-weight: 700; font-size: clamp(22px, 2.6vw, 32px); line-height: 1.05; letter-spacing: -0.02em; color: var(--tl-ink); }
.tl-sub { margin: 7px 0 0; font-size: 14px; color: var(--tl-ink2); }
/* The scope pill sits on the title's line, beside the pair it describes (Rossi, 29 Sep: one line less in a
   dense header). A white fill with no border, so it reads as a label and not as one of the bordered filter
   buttons below it; it drops under the title when the line is full. */
.tl-titlerow { display: flex; align-items: center; flex-wrap: wrap; column-gap: 14px; row-gap: 6px; }
.tl-scope { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 500; line-height: 1.4; white-space: nowrap; color: var(--tl-ink2); background: var(--tl-card); border-radius: 999px; padding: 3px 10px 3px 8px; }
/* the platform's own logo in front of its name, the same one the platform menu shows (Rossi, 29 Sep) */
.tl-scope .tl-pi { width: 14px; height: 14px; }
/* the title keeps its line and the follower counter stays beside it: the title box takes only what is
   left, and the pill is what drops under the title (a tablet in the board, 29 Sep) */
@media (min-width: 641px) {
  .tl-titlebox { flex: 1 1 0%; }
  .tl-title { white-space: nowrap; }
}
/* Signed out, nothing fills it, and the empty pill showed as a stray blob under the title (live, 26 Sep). */
.tl-scope:empty { display: none; }

/* ── the two-brand picker (2026-09-28): the names in the title are buttons ─────────────────────── */
.tl-titlebox { position: relative; min-width: 0; }
.tl-pick { display: inline-flex; align-items: center; gap: 6px; max-width: 100%; padding: 0 4px 2px; margin: 0 -4px; background: none; border: 0; border-bottom: 2px dashed var(--tl-rule2); border-radius: 4px 4px 0 0; font-weight: inherit; letter-spacing: inherit; line-height: inherit; text-align: left; }
.tl-pick span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tl-pick:hover, .tl-pick.open { background: var(--tl-tag); border-bottom-color: var(--tl-ink3); }
.tl-pick .tl-chev { flex-shrink: 0; color: var(--tl-ink3); transition: transform .15s ease; }
.tl-pick.open .tl-chev { transform: rotate(180deg); }
.tl-vs { font-weight: 500; color: var(--tl-ink3); }
/* No top: it sits where it would in the flow, straight under the title; the script sets left. */
.tl-pickmenu { position: absolute; z-index: 30; left: 0; margin-top: 6px; width: min(340px, calc(100vw - 32px)); background: var(--tl-card); border: 1px solid var(--tl-rule); border-radius: 12px; box-shadow: 0 12px 32px var(--tl-shadow); padding: 8px; }
.tl-pick-q { width: 100%; height: 40px; padding: 0 12px; font: inherit; font-size: 14px; color: var(--tl-ink); background: var(--tl-canvas); border: 1px solid var(--tl-rule); border-radius: 8px; }
.tl-pick-q:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: 1px; }
.tl-pick-list { list-style: none; margin: 6px 0 0; padding: 0; max-height: 300px; overflow-y: auto; }
.tl-pick-opt { display: flex; align-items: center; gap: 10px; min-height: 44px; padding: 6px 8px; border-radius: 8px; cursor: pointer; }
.tl-pick-opt:hover, .tl-pick-opt.active { background: var(--tl-tag); }
.tl-pick-opt[aria-selected="true"] .tl-pick-name { font-weight: 700; }
.tl-pick-opt img, .tl-pick-ph { width: 24px; height: 24px; border-radius: 50%; flex-shrink: 0; object-fit: cover; background: var(--tl-media); }
.tl-pick-ph { display: inline-flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700; color: var(--tl-ink2); }
.tl-pick-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 14px; color: var(--tl-ink); }
.tl-pick-meta { flex-shrink: 0; font-size: 12px; color: var(--tl-ink3); }
.tl-pick-empty { padding: 12px 8px; font-size: 13px; color: var(--tl-ink2); }
.tl-pick-note { margin: 6px 8px 2px; font-size: 12px; color: var(--tl-ink3); }

.tl-counter { display: grid; grid-template-columns: auto auto auto; column-gap: 16px; row-gap: 3px; align-items: baseline; }
/* the week is a caption to the numbers, not a heading: the board's muted grey, which still clears 4.5:1
   (Rossi, 29 Sep: the page is dense, make it greyer) */
.tl-counter .wk { grid-column: 1 / -1; font-size: 12px; font-weight: 500; color: var(--tl-ink3); }
.tl-counter .nm { display: flex; align-items: center; gap: 7px; font-weight: 600; font-size: 14px; color: var(--tl-ink); }
.tl-counter .val { font-weight: 700; font-size: 19px; text-align: right; font-variant-numeric: tabular-nums; color: var(--tl-ink); }
.tl-counter .chg { font-size: 12.5px; font-weight: 500; text-align: right; color: var(--tl-ink2); font-variant-numeric: tabular-nums; }
.tl-counter .chg.up { color: var(--tl-up); }
.tl-counter .chg.down { color: var(--tl-down); }

.tl-sw { display: inline-block; width: 11px; height: 11px; border-radius: 3px; flex-shrink: 0; }
.tl-sw.l0 { background: var(--tl-lane-0); }
.tl-sw.l1 { background: var(--tl-lane-1); }
/* the brand's logo in place of the colour square (Rossi, 28 Sep: "display the respective brand icon"),
   ringed in its lane colour so the two lanes still read apart; a logo that fails to load drops the
   'logo' class and leaves the square */
.tl-sw.logo { width: 18px; height: 18px; border-radius: 50%; background: var(--tl-card); overflow: hidden; }
.tl-sw.logo.l0 { box-shadow: 0 0 0 1.5px var(--tl-lane-0); }
.tl-sw.logo.l1 { box-shadow: 0 0 0 2px var(--tl-lane-1); }
.tl-sw.logo img { width: 100%; height: 100%; object-fit: cover; display: block; border-radius: 50%; }
.tl-lane-label .tl-sw.logo { width: 24px; height: 24px; }
.tl-lbl .tl-sw.logo { width: 16px; height: 16px; vertical-align: -3px; }
/* a card's dot on the line: the same logo, with a canvas gap so the axis does not run through it */
.tl-adot { position: absolute; width: 20px; height: 20px; pointer-events: none; }
.tl-adot .tl-sw { width: 20px; height: 20px; border-radius: 50%; outline: 2px solid var(--tl-canvas); outline-offset: 2px; }
.tl-adot .tl-sw.logo.l0 { box-shadow: 0 0 0 2px var(--tl-lane-0); }
.tl-adot .tl-sw.logo.l1 { box-shadow: 0 0 0 2.5px var(--tl-lane-1); }

/* ── controls ───────────────────────────────────────────────────────────────────────────────── */
.tl-controls { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 0 24px 8px; flex-wrap: wrap; }
.tl-chips, .tl-zoom, .tl-right { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.tl-right { gap: 10px; }
.tl-chip { height: 36px; padding: 0 14px; border-radius: 999px; border: 1px solid var(--tl-rule); background: var(--tl-card); color: var(--tl-ctl); font-size: 13px; font-weight: 500; white-space: nowrap; }
.tl-chip:hover { border-color: var(--tl-ctl); }
.tl-chip[aria-pressed="true"] { background: var(--tl-btn-bg); color: var(--tl-btn-fg); border-color: var(--tl-btn-bg); }
.tl-seg { display: flex; padding: 2px; border-radius: 8px; background: var(--tl-card); border: 1px solid var(--tl-rule); }
.tl-seg button { height: 32px; padding: 0 11px; border: 0; border-radius: 6px; background: transparent; color: var(--tl-ctl); font-size: 13px; font-weight: 500; white-space: nowrap; }
.tl-seg button:hover { background: var(--tl-canvas); }
.tl-seg button[aria-pressed="true"] { background: var(--tl-canvas); color: var(--tl-ctl); font-weight: 700; }
.tl-icon { width: 36px; height: 36px; border-radius: 8px; border: 1px solid var(--tl-rule); background: var(--tl-card); font-size: 19px; font-weight: 600; display: flex; align-items: center; justify-content: center; color: var(--tl-ctl); }
.tl-icon:hover { border-color: var(--tl-ctl); }
.tl-select { height: 36px; padding: 0 30px 0 10px; border: 1px solid var(--tl-rule); border-radius: 8px; background: var(--tl-card); color: var(--tl-ctl); font: inherit; font-size: 13px; font-weight: 500; }
/* the platform menu: the board's logos beside each name (a native list cannot show them) */
.tl-platwrap { position: relative; }
.tl-platbtn { display: inline-flex; align-items: center; gap: 8px; min-width: 150px; padding: 0 10px; text-align: left; }
.tl-platbtn:hover, .tl-platbtn.open { border-color: var(--tl-rule2); }
.tl-platbtn:focus-visible, .tl-plat-opt:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: 2px; }
.tl-plat-name { flex: 1; white-space: nowrap; }
.tl-platbtn .tl-chev { flex-shrink: 0; color: var(--tl-ink3); transition: transform .15s ease; }
.tl-platbtn.open .tl-chev { transform: rotate(180deg); }
.tl-pi { width: 16px; height: 16px; flex-shrink: 0; display: inline-flex; }
.tl-pi svg { width: 100%; height: 100%; display: block; }
.tl-pi.all { color: var(--tl-ink2); }
.tl-platmenu { position: absolute; z-index: 30; right: 0; top: 100%; margin: 6px 0 0; padding: 6px; list-style: none; min-width: 100%; width: max-content; background: var(--tl-card); border: 1px solid var(--tl-rule); border-radius: 12px; box-shadow: 0 12px 32px var(--tl-shadow); }
.tl-plat-opt { display: flex; align-items: center; gap: 10px; min-height: 40px; padding: 6px 10px; border-radius: 8px; font-size: 13.5px; color: var(--tl-ink); cursor: pointer; white-space: nowrap; }
.tl-plat-opt:hover, .tl-plat-opt:focus { background: var(--tl-tag); outline: none; }
.tl-plat-opt[aria-selected="true"] { font-weight: 700; }
.tl-playbtn { height: 36px; padding: 0 15px 0 12px; border-radius: 999px; border: 0; background: var(--tl-btn-bg); color: var(--tl-btn-fg); font-size: 14px; font-weight: 600; display: flex; align-items: center; gap: 8px; white-space: nowrap; }

/* ── the timeline itself ────────────────────────────────────────────────────────────────────── */
.tl-stage { position: relative; flex-grow: 1; min-height: 420px; touch-action: none; user-select: none; -webkit-user-select: none; cursor: grab; overflow: hidden; }
.tl-stage.dragging { cursor: grabbing; }
.tl-stage > svg { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.tl-layer { position: absolute; inset: 0; pointer-events: none; }

.tl-card { position: absolute; background: var(--tl-card); border: 1px solid var(--tl-rule); border-radius: 10px; padding: 9px 12px; display: flex; flex-direction: column; gap: 4px; overflow: hidden; pointer-events: auto; cursor: pointer; box-shadow: 0 1px 2px var(--tl-shadow); }
.tl-card:hover { border-color: var(--tl-rule2); }
.tl-card.big { border-radius: 12px; border-top-width: 3px; }
.tl-card.l0.big { border-top-color: var(--tl-lane-0); }
.tl-card.l1.big { border-top-color: var(--tl-lane-1); }
.tl-card.ms { border-radius: 32px; border-width: 1.5px; justify-content: center; padding: 8px 14px; }
.tl-card.ms.l0 { border-color: var(--tl-lane-0); }
.tl-card.ms.l1 { border-color: var(--tl-lane-1); }
.tl-card .top { display: flex; justify-content: space-between; align-items: center; gap: 8px; font-size: 12px; font-weight: 500; color: var(--tl-ink2); }
.tl-tag { padding: 2px 8px; border-radius: 999px; background: var(--tl-tag); font-size: 11.5px; font-weight: 500; white-space: nowrap; color: var(--tl-ink2); }
/* GenevAI (28 Oct 2026): a programme card's tag names its day ("Votre programme du 6 nov."), longer than "Votre carte";
   on a phone's narrow column it ran past the card's edge, so it may wrap */
.tl-vcardcard.plan .tl-tag { white-space: normal; line-height: 1.3; }
/* flex-shrink 0: when a card is short of room the headline must not be squeezed and cut mid-line;
   the card sizes in timeline.js leave room for two full lines. */
/* 13.5px on a small card (was 14.5, Rossi 28 Sep: "shouldnt we adapt the font size of the smaller
   cards a bit?"): a small card now reads as second to a large one (17px), and more headlines fit
   in two lines. The card heights follow on their own: each headline is measured in this font. */
.tl-ttl { all: unset; cursor: pointer; font-weight: 600; font-size: 13.5px; line-height: 1.2; color: var(--tl-ink); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; flex-shrink: 0; }
.tl-ttl:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: 2px; border-radius: 3px; }
/* a card timeline.js grew for a longer headline (fitFor): the clamp opens by the same lines */
.tl-card.ttl3 .tl-ttl { -webkit-line-clamp: 3; }
.tl-card.ttl4 .tl-ttl { -webkit-line-clamp: 4; }
/* the off-screen box timeline.js lays a headline out in, to size its card (extraFor) */
.tl-measure { position: absolute; left: 0; top: 0; visibility: hidden; pointer-events: none; }
.tl-measure .tl-ttl { display: block; -webkit-line-clamp: none; overflow: visible; }
.big .tl-ttl { font-weight: 700; font-size: 17px; letter-spacing: -0.01em; }
.compact .tl-ttl { font-size: 12.5px; }
.ms .tl-ttl { font-weight: 700; font-size: 14px; }
.tl-sum { font-size: 12.5px; color: var(--tl-ink2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tl-bot { margin-top: auto; display: flex; justify-content: space-between; align-items: baseline; gap: 8px; font-size: 12.5px; color: var(--tl-ink2); }
.tl-bot b { color: var(--tl-ink); font-variant-numeric: tabular-nums; }
.tl-bot a { color: var(--tl-ink); font-weight: 600; text-decoration: underline; text-underline-offset: 2px; pointer-events: auto; }
.tl-when { font-size: 12px; color: var(--tl-ink3); }
.tl-repost { font-size: 11px; color: var(--tl-ink3); }
.tl-media { height: 84px; flex-shrink: 0; border-radius: 8px; background: var(--tl-media); overflow: hidden; }
.tl-media img { width: 100%; height: 100%; object-fit: cover; display: block; }

.tl-lane-label { position: absolute; display: flex; align-items: center; gap: 9px; font-weight: 700; font-size: 18px; color: var(--tl-ink); pointer-events: none; }
.tl-lbl { position: absolute; font-size: 12px; font-weight: 500; color: var(--tl-ink3); white-space: nowrap; pointer-events: none; }
.tl-lbl.today { color: var(--tl-ink); font-weight: 700; }
.tl-note { position: absolute; font-size: 12px; font-weight: 500; color: var(--tl-ink2); background: var(--tl-canvas); border: 1px solid var(--tl-rule); border-radius: 999px; padding: 5px 11px; white-space: nowrap; pointer-events: none; }
.tl-strip-title { position: absolute; left: 24px; width: 104px; pointer-events: none; }
.tl-strip-title b { display: block; font-size: 14px; font-weight: 700; color: var(--tl-ink); }
.tl-strip-title span { display: block; font-size: 11.5px; line-height: 1.3; white-space: nowrap; color: var(--tl-ink3); }   /* one line (Rossi, 29 Sep) */
.tl-strip-title.narrow { width: auto; white-space: nowrap; }
.tl-readout { position: absolute; background: var(--tl-card); border: 1px solid var(--tl-ink); border-radius: 8px; padding: 6px 10px; font-size: 12.5px; display: flex; flex-direction: column; gap: 2px; pointer-events: none; white-space: nowrap; color: var(--tl-ink); }
.tl-readout .w { font-size: 11.5px; font-weight: 600; color: var(--tl-ink2); }
.tl-readout .r { display: flex; align-items: center; gap: 6px; }
/* a canvas-coloured backing so the Today line runs behind the label, not through the name */
.tl-endlbl { position: absolute; display: flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 600; white-space: nowrap; color: var(--tl-ink); pointer-events: none; background: var(--tl-canvas); border-radius: 4px; padding: 1px 5px; }
.tl-endlbl .up { color: var(--tl-up); }
.tl-endlbl .down { color: var(--tl-down); }

/* svg — every stroke and fill is a role variable */
.g-month { stroke: var(--tl-rule); stroke-width: 1; }
.g-minor { stroke: var(--tl-rule); stroke-width: 1; stroke-dasharray: 2 4; }
.g-future { fill: var(--tl-future); }
.g-today { stroke: var(--tl-today); stroke-width: 1.5; }
.g-axis { stroke: var(--tl-axis); stroke-width: 2; }
.g-axis-dot { fill: var(--tl-axis); }
.g-stem.l0 { stroke: var(--tl-lane-0); stroke-width: 2; }
.g-stem.l1 { stroke: var(--tl-lane-1); stroke-width: 2; }
.g-dot.l0 { fill: var(--tl-lane-0); stroke: var(--tl-canvas); stroke-width: 2; }
.g-dot.l1 { fill: var(--tl-lane-1); stroke: var(--tl-canvas); stroke-width: 2; }
/* an ordinary post: a dot hugging the line on its brand's side, never a line (a line from the axis is a card's stem;
   Rossi, 30 Sep: "these lines seem to be linked to a card, but there is no card") */
.g-post.l0 { fill: var(--tl-lane-0); opacity: .6; }
.g-post.l1 { fill: var(--tl-lane-1); opacity: .6; }
/* a key moment with no room for its card: a ring on the line, on its brand's side, that names it and shows it on a
   press (27px to aim at); filled when it is the moment being discussed */
.tl-bead { position: absolute; width: 11px; height: 11px; margin: -5.5px 0 0 -5.5px; padding: 0; border-radius: 50%; background: var(--tl-canvas); border: 2px solid var(--tl-lane-0); pointer-events: auto; cursor: pointer; }
.tl-bead.l1 { border-color: var(--tl-lane-1); }
.tl-bead::after { content: ''; position: absolute; inset: -8px; }
.tl-bead:hover, .tl-bead:focus-visible, .tl-bead.discussed { background: var(--tl-lane-0); }
.tl-bead.l1:hover, .tl-bead.l1:focus-visible, .tl-bead.l1.discussed { background: var(--tl-lane-1); }
.tl-bead:focus-visible { outline: 2px solid var(--tl-ink); outline-offset: 2px; }
.tl-bead.discussed { width: 15px; height: 15px; margin: -7.5px 0 0 -7.5px; }
/* ...and an ordinary post's card, zoomed in to a week or closer: quieter than a key moment's, as on the phone's list */
.tl-card.post { box-shadow: none; }
.tl-card.post .tl-ttl { font-weight: 500; }
.g-hatch { stroke: var(--tl-hatch); stroke-width: 3; }
.g-sep { stroke: var(--tl-rule); stroke-width: 1; }
.g-base { stroke: var(--tl-rule2); stroke-width: 1; }
.g-line.l0 { fill: none; stroke: var(--tl-lane-0); stroke-width: 2; stroke-linejoin: round; }
.g-line.l1 { fill: none; stroke: var(--tl-lane-1); stroke-width: 2.5; stroke-linejoin: round; }
.g-hover { stroke: var(--tl-ink2); stroke-width: 1; stroke-dasharray: 4 4; }
.g-hdot.l0 { fill: var(--tl-canvas); stroke: var(--tl-lane-0); stroke-width: 2; }
.g-hdot.l1 { fill: var(--tl-canvas); stroke: var(--tl-lane-1); stroke-width: 2.5; }
.g-playhead { stroke: var(--tl-play); stroke-width: 2; }

/* play mode: every other card steps back */
.tl-stage.playing .tl-card:not(.current) { opacity: .32; }
.tl-card.current { box-shadow: 0 0 0 3px var(--tl-focus); }
.tl-card.current.l0 { box-shadow: 0 0 0 3px var(--tl-lane-0); }
.tl-card.current.l1 { box-shadow: 0 0 0 3px var(--tl-lane-1); }
/* the card being discussed in the call panel: a thinner ring in its brand's colour (the story's own
   moment keeps the thicker one when both fall on the same card) */
.tl-card.discussed:not(.current).l0, .tl-vcard.discussed:not(.current).l0 { box-shadow: 0 0 0 2px var(--tl-lane-0); }
.tl-card.discussed:not(.current).l1, .tl-vcard.discussed:not(.current).l1 { box-shadow: 0 0 0 2px var(--tl-lane-1); }
/* During a call the analyst leads, not the paused story (Rossi, 30 Sep, after his first real calls: "when the voice
   agent is talking, the cards or the events on the timeline have like a white layer overlay"): starting a call only
   paused the story, so every card but the story's own stepped back for the whole call, the one being talked about too.
   Now nothing steps back, the story's ring gives way, and the moment being discussed wears the thick one. */
.tl-app.on-call .tl-stage.playing .tl-card:not(.current), .tl-app.on-call .tl-vlist.playing .tl-vcard:not(.current) { opacity: 1; }
.tl-app.on-call .tl-card.current:not(.discussed), .tl-app.on-call .tl-vcard.current:not(.discussed) { box-shadow: 0 1px 2px var(--tl-shadow); }
.tl-app.on-call .tl-card.discussed.l0, .tl-app.on-call .tl-vcard.discussed.l0 { box-shadow: 0 0 0 3px var(--tl-lane-0); }
.tl-app.on-call .tl-card.discussed.l1, .tl-app.on-call .tl-vcard.discussed.l1 { box-shadow: 0 0 0 3px var(--tl-lane-1); }

/* ── player bar (replaces the footer while playing) ─────────────────────────────────────────── */
.tl-player { border-top: 1px solid var(--tl-rule); background: var(--tl-card); padding: 0 24px 12px; }
/* the yellow runs on the board's dark blue, the pairing of Play the story itself (review, 30 Sep: 3px of yellow on the
   light grey track was the faintest thing in the player; yellow on the dark blue is 5.7 to 1) */
.tl-prog { height: 4px; margin: 0 -24px 10px; background: var(--tl-prog-track); }
.tl-prog div { height: 4px; width: 0; background: var(--tl-play); transition: width .3s; }
.tl-prow { display: flex; align-items: center; gap: 20px; justify-content: space-between; }
.tl-cap { min-width: 0; flex: 1; }
.tl-capmeta { font-size: 12px; font-weight: 600; color: var(--tl-ink3); }
.tl-captext { font-size: 17px; line-height: 1.35; font-weight: 500; color: var(--tl-ink); margin-top: 2px; }
.tl-pctl { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.tl-rb { width: 44px; height: 44px; border-radius: 22px; border: 1px solid var(--tl-rule); background: var(--tl-card); display: flex; align-items: center; justify-content: center; color: var(--tl-ink); }
.tl-rb.main { width: 48px; height: 48px; border-radius: 24px; background: var(--tl-btn-bg); color: var(--tl-btn-fg); border: 0; }
.tl-tgl { height: 36px; padding: 0 12px; border-radius: 999px; border: 1px solid var(--tl-rule); background: var(--tl-card); color: var(--tl-ink2); font-size: 13px; font-weight: 500; white-space: nowrap; }
.tl-tgl[aria-pressed="true"] { background: var(--tl-btn-bg); color: var(--tl-btn-fg); border-color: var(--tl-btn-bg); }
.tl-spd-label { font-size: 12px; font-weight: 500; color: var(--tl-ink3); margin-left: 4px; }

.tl-foot { display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap; padding: 8px 24px 14px; font-size: 12.5px; color: var(--tl-ink2); }
.tl-foot b { color: var(--tl-ink); }
.tl-how-narrow { display: none; }   /* a phone gets the short how-to; the long one wrapped to five lines */

/* ── loading, sign-in, error and empty states: each says WHY it is empty ────────────────────── */
.tl-state { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; padding: 24px; background: var(--tl-canvas); z-index: 5; }
.tl-state-box { max-width: 420px; text-align: center; background: var(--tl-card); border: 1px solid var(--tl-rule); border-radius: 12px; padding: 26px 26px 22px; box-shadow: 0 4px 12px var(--tl-shadow); }
.tl-state-box h2 { margin: 0 0 8px; font-size: 19px; font-weight: 700; color: var(--tl-ink); }
.tl-state-box p { margin: 0 0 16px; font-size: 14px; line-height: 1.5; color: var(--tl-ink2); }
.tl-state-box button, .tl-state-box a.tl-cta { display: inline-flex; align-items: center; justify-content: center; height: 40px; padding: 0 18px; border-radius: 999px; border: 0; background: var(--tl-btn-bg); color: var(--tl-btn-fg); font-weight: 600; font-size: 14px; text-decoration: none; }
.tl-spinner { width: 26px; height: 26px; border: 3px solid var(--tl-rule); border-top-color: var(--tl-play); border-radius: 50%; margin: 0 auto 12px; animation: tl-spin .8s linear infinite; }
@keyframes tl-spin { to { transform: rotate(360deg); } }

/* ── phones (§7): one scrolling row of chips, presets are the zoom, labels inside the lanes ─── */
/* Measured on a 390 x 844 phone (2026-09-26): header, chips, platform menu, presets and the play
   button stacked to 380px, so the timeline started at 435px and, while playing, the caption and
   the pause button fell below the screen. Now: platform menu and play share a row, the presets
   take the next one, the scope pill goes (the menu and the counter already say it), the controls
   step aside while the story plays, and a phone shorter than all of it scrolls. */
@media (max-width: 640px) {
  .tl-app { height: auto; flex: 1 0 auto; }   /* fills a flex host, never shrinks below its content */
  .tl-head { padding: 12px 14px 6px; gap: 10px; }
  .tl-scope { display: none; }
  .tl-controls { padding: 0 14px 6px; flex-direction: column; align-items: stretch; gap: 8px; flex-wrap: nowrap; }
  .tl-app.playing .tl-controls { display: none; }
  .tl-chips { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; margin: 0 -14px; padding: 0 14px; }
  .tl-chips::-webkit-scrollbar { display: none; }
  .tl-chip, .tl-seg button, .tl-select, .tl-playbtn, .tl-tgl { min-height: 44px; }
  .tl-icon { display: none; }
  .tl-right { display: grid; grid-template-columns: 1fr auto; grid-template-areas: "plat play" "zoom zoom"; gap: 8px; }
  .tl-right .tl-platwrap { grid-area: plat; min-width: 0; }
  .tl-right .tl-select { width: 100%; }
  .tl-platmenu { left: 0; right: auto; width: 100%; }
  .tl-plat-opt { min-height: 44px; }
  .tl-right .tl-playbtn { grid-area: play; justify-content: center; }
  .tl-right .tl-zoom { grid-area: zoom; }
  .tl-zoom .tl-seg { flex: 1; }
  .tl-zoom .tl-seg button { flex: 1; padding: 0 6px; }
  .tl-counter .val { font-size: 16px; }
  .tl-player { padding: 0 14px 10px; }
  .tl-prog { margin: 0 -14px 8px; }
  .tl-prow { flex-direction: column; align-items: stretch; gap: 8px; }
  .tl-captext { font-size: 15px; }
  .tl-pctl { flex-wrap: wrap; gap: 6px; }
  .tl-spd-label { display: none; }
  .tl-foot { padding: 6px 14px 10px; gap: 2px; }
  .tl-how-wide { display: none; }
  .tl-how-narrow { display: inline; }
  .tl-lane-label { font-size: 14px; }
  .tl-strip-title { left: 14px; }
}

/* ══ THE VERTICAL PHONE LAYOUT (handoff 2026-09-28) ══════════════════════════════════════════════
   Keyed on the class the script sets (.tl-app.vertical), never on a width rule, so a 9:16 recording at
   1080px and a narrow timeline on a wider screen get it too. Colours are the roles above, nothing else. */
.tl-app.vertical { height: auto; flex: 1 0 auto; }
.tl-app.vertical .tl-head { padding: 12px 14px 6px; gap: 6px; flex-wrap: nowrap; justify-content: flex-start; align-items: flex-start; }
.tl-back { flex-shrink: 0; width: 44px; height: 44px; margin: -8px 0 0 -12px; border: 0; background: none; border-radius: 22px; display: inline-flex; align-items: center; justify-content: center; color: var(--tl-ink); }
.tl-app.vertical .tl-titlebox { flex: 1; }
/* the wide layout keeps its title on one line; a phone-width timeline on a wider screen must not */
.tl-app.vertical .tl-title { font-size: 22px; white-space: normal; }
.tl-app.vertical .tl-pick { min-height: 44px; }
/* the brand list lines up with the page's edges (the script moves it past the Back button) */
.tl-app.vertical .tl-pickmenu { width: calc(100vw - 28px); max-width: 360px; }
/* loading, error and empty messages sit in view at the top of the list, not centred in a long one */
.tl-app.vertical .tl-state { position: relative; inset: auto; min-height: 50vh; align-items: flex-start; padding-top: 40px; }
.tl-app.vertical .tl-sub { margin-top: 4px; font-size: 13px; }
.tl-app.vertical .tl-scope, .tl-app.vertical .tl-counter, .tl-app.vertical .tl-how-wide, .tl-app.vertical .tl-how-narrow,
.tl-app.vertical .tl-playbtn, .tl-app.vertical .tl-icon, .tl-app.vertical .tl-plat-name { display: none; }
/* one row of controls: the platform button (its logo), then the filter chips scrolling sideways */
.tl-app.vertical .tl-controls { padding: 0 0 8px 14px; flex-direction: row; align-items: center; gap: 8px; flex-wrap: nowrap; }
.tl-app.vertical .tl-right { order: -1; display: flex; flex-shrink: 0; }
.tl-app.vertical .tl-chips { flex: 1; min-width: 0; flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; margin: 0; padding: 0 14px 0 0; }
.tl-app.vertical .tl-chips::-webkit-scrollbar { display: none; }
.tl-app.vertical .tl-chip, .tl-app.vertical .tl-seg button, .tl-app.vertical .tl-select { min-height: 44px; }
.tl-app.vertical .tl-platbtn { min-width: 0; width: auto; padding: 0 10px; gap: 6px; }
.tl-app.vertical .tl-platmenu { left: 0; right: auto; width: max-content; min-width: 200px; }
.tl-app.vertical .tl-plat-opt { min-height: 44px; }

.tl-vbody { position: relative; min-height: 60vh; padding: 0 14px; }
/* what stays at the top while the list scrolls: the level buttons and the follower header (not announced:
   it would read out on every scroll) */
.tl-vsticky { position: sticky; top: 0; z-index: 4; margin: 0 -14px; background: var(--tl-canvas); border-bottom: 1px solid var(--tl-rule); }
.tl-vtools { padding: 6px 14px 0; }
.tl-vtools .tl-zoom { display: flex; }
.tl-vtools .tl-seg { flex: 1; min-width: 0; }
.tl-vtools .tl-seg button { flex: 1; min-width: 0; padding: 0 4px; font-size: 12.5px; }
.tl-vhead { padding: 6px 14px 8px; display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); column-gap: 12px; min-height: 78px; }
.tl-vhead:empty { display: none; }
.tl-vh-wk { grid-column: 1 / -1; text-align: center; font-size: 12px; font-weight: 600; color: var(--tl-ink2); margin-bottom: 2px; }
.tl-vh-b { min-width: 0; }
.tl-vh-b.l1 { text-align: right; }
.tl-vh-nm { display: flex; align-items: center; gap: 6px; min-width: 0; font-size: 13px; font-weight: 600; color: var(--tl-ink); }
.tl-vh-b.l1 .tl-vh-nm { justify-content: flex-end; }
.tl-vh-nm span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tl-vh-val { font-size: 20px; font-weight: 700; line-height: 1.15; font-variant-numeric: tabular-nums; color: var(--tl-ink); }
.tl-vh-chg { min-height: 15px; font-size: 11.5px; font-weight: 500; font-variant-numeric: tabular-nums; color: var(--tl-ink2); }
.tl-vh-chg.up { color: var(--tl-up); }
.tl-vh-chg.down { color: var(--tl-down); }

/* the line runs down the middle; each day is a row: first brand | date on the line | second brand */
.tl-vlist { position: relative; list-style: none; margin: 0; padding: 12px 0 0; }
.tl-vlist::before { content: ''; position: absolute; left: 50%; top: 0; bottom: 0; width: 2px; margin-left: -1px; background: var(--tl-axis); }
/* no moments (signed out, under "Sign in to see the timeline"): no 12px stub of the line on its own (13 Oct design pass) */
.tl-vlist:empty::before { display: none; }
/* the gap above a row is its own top padding (set by the script), and a little room on the other sides:
   a row laid out only near the screen clips anything painted outside its box (labels, focus rings) */
.tl-vrow { position: relative; display: grid; grid-template-columns: minmax(0, 1fr) 48px minmax(0, 1fr); align-items: start; margin: 0 -4px; padding-right: 4px; padding-bottom: 4px; padding-left: 4px; }
.tl-vlist.virtual > .tl-vrow { content-visibility: auto; contain-intrinsic-size: auto var(--vh, 120px); }
.tl-vcol { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.tl-vgut { position: relative; display: flex; flex-direction: column; align-items: center; padding-top: 2px; min-height: 48px; }
.tl-vdate { position: relative; z-index: 1; width: 100%; padding: 2px 0 3px; background: var(--tl-canvas); font-size: 11px; font-weight: 600; line-height: 1.15; text-align: center; color: var(--tl-ink2); }
.tl-vrow.ms .tl-vdate { font-weight: 500; }
.tl-vdot { position: relative; z-index: 1; margin-top: 3px; width: 22px; height: 22px; display: flex; align-items: center; justify-content: center; border-radius: 50%; background: var(--tl-canvas); }
.tl-vdot .tl-sw { width: 18px; height: 18px; border-radius: 50%; }
/* two brands on one day: a hollow ring (ink would read as the first brand) and the words "Same day" */
.tl-vdot.same { width: 14px; height: 14px; margin-top: 7px; border: 2.5px solid var(--tl-ink2); }
.tl-vsame { position: relative; z-index: 1; margin-top: 3px; padding: 1px 2px; background: var(--tl-canvas); font-size: 9.5px; font-weight: 700; line-height: 1.1; text-align: center; text-transform: uppercase; letter-spacing: .02em; color: var(--tl-ink2); }
/* the connector: a short line in the brand's colour from the dot to its card */
.tl-vrow.has-l .tl-vdot::before, .tl-vrow.has-r .tl-vdot::after { content: ''; position: absolute; top: 50%; height: 2px; margin-top: -1px; width: 13px; }
.tl-vrow.has-l .tl-vdot::before { right: 100%; background: var(--tl-lane-0); }
.tl-vrow.has-r .tl-vdot::after { left: 100%; background: var(--tl-lane-1); }
.tl-vrow.has-l .tl-vdot.same::before, .tl-vrow.has-r .tl-vdot.same::after { width: 17px; }
.tl-vlater { position: absolute; left: 50%; z-index: 1; transform: translateX(-50%); padding: 1px 6px; white-space: nowrap; background: var(--tl-canvas); font-size: 10.5px; font-weight: 500; color: var(--tl-ink2); }

.tl-vcard { all: unset; box-sizing: border-box; display: flex; flex-direction: column; justify-content: center; gap: 4px; width: 100%; min-width: 0; min-height: 44px; padding: 8px 10px; background: var(--tl-card); border: 1px solid var(--tl-rule); border-radius: 10px; box-shadow: 0 1px 2px var(--tl-shadow); color: var(--tl-ink); text-align: left; overflow-wrap: anywhere; cursor: pointer; }
.tl-vcard:focus-visible, .tl-vchip:focus-visible, .tl-vmore:focus-visible, .tl-vmorem:focus-visible, .tl-vplay:focus-visible, .tl-back:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: 2px; }
.tl-vcard.big { border-top-width: 3px; border-radius: 12px; }
.tl-vcard.big.l0 { border-top-color: var(--tl-lane-0); }
.tl-vcard.big.l1 { border-top-color: var(--tl-lane-1); }
.tl-vcard.ms { align-items: center; padding: 8px 12px; border-radius: 999px; border-width: 1.5px; text-align: center; }
.tl-vcard.ms.l0 { border-color: var(--tl-lane-0); }
.tl-vcard.ms.l1 { border-color: var(--tl-lane-1); }
.tl-vcard.tick { box-shadow: none; }
.tl-vtop { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }
.tl-vtop .tl-tag { max-width: 100%; overflow: hidden; text-overflow: ellipsis; padding: 1px 7px; font-size: 11px; }
.tl-vrep { font-size: 10.5px; color: var(--tl-ink3); }
.tl-vttl { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; font-size: 13px; font-weight: 600; line-height: 1.22; }
.tl-vcard.big .tl-vttl { font-size: 15px; font-weight: 700; letter-spacing: -0.01em; }
.tl-vcard.ms .tl-vttl { font-weight: 700; }
.tl-vcard.tick .tl-vttl { font-weight: 500; }
.tl-vbot { font-size: 12px; color: var(--tl-ink2); }
.tl-vbot b { color: var(--tl-ink); font-variant-numeric: tabular-nums; }
.tl-vcard.big .tl-vbot { font-weight: 700; color: var(--tl-ink); }
.tl-vmedia { position: relative; display: block; margin: -2px -4px 2px; aspect-ratio: 16 / 9; overflow: hidden; border-radius: 7px; background: var(--tl-media); }
/* a post whose only media is a video we host shows its first frame in the same frame (phoneCover) */
.tl-vmedia img, .tl-vmedia .tl-vcover { width: 100%; height: 100%; object-fit: cover; display: block; }
/* small pills with a full 44px tap area: the button is the tap area, the pill is drawn inside it */
.tl-vmore, .tl-vchip, .tl-vmorem { all: unset; box-sizing: border-box; min-height: 44px; display: flex; align-items: center; justify-content: center; cursor: pointer; }
.tl-vmore > span, .tl-vchip > span, .tl-vmorem > span { padding: 4px 11px; border: 1px dashed var(--tl-rule2); border-radius: 999px; background: var(--tl-card); font-size: 12px; font-weight: 600; line-height: 1.2; color: var(--tl-ink2); white-space: nowrap; }
.tl-vchip.l0 { justify-content: flex-end; }
.tl-vchip.l1 { justify-content: flex-start; }
.tl-vrow.chips { padding-top: 8px; }
.tl-vrow.chips .tl-vgut { min-height: 0; }

/* month dividers, "Posts tracked from" on the not-tracked-yet hatch, and Today */
.tl-vmark { position: relative; display: flex; flex-direction: column; align-items: center; gap: 6px; margin: 22px 0 4px; text-align: center; }
.tl-vmark > span { position: relative; z-index: 1; padding: 4px 12px; background: var(--tl-canvas); font-size: 12.5px; font-weight: 700; color: var(--tl-ink); }
.tl-vmark.month > span { border: 1px solid var(--tl-rule); border-radius: 999px; background: var(--tl-card); }
.tl-vmark .tl-vmorem { position: relative; z-index: 1; }
.tl-vmark.tracked { margin: 14px -14px 4px; padding: 16px 0; background: repeating-linear-gradient(45deg, var(--tl-hatch) 0 2px, transparent 2px 9px); }
.tl-vmark.tracked > span { border-radius: 999px; font-weight: 600; color: var(--tl-ink2); }
.tl-vmark.today::before { content: ''; position: absolute; left: -14px; right: -14px; top: 50%; border-top: 2px solid var(--tl-today); }
.tl-vmark.today { margin-top: 26px; }

/* the story on a phone: the other cards step back */
.tl-vlist.playing .tl-vcard:not(.current) { opacity: .4; }
.tl-vcard.current.l0 { box-shadow: 0 0 0 3px var(--tl-lane-0); }
.tl-vcard.current.l1 { box-shadow: 0 0 0 3px var(--tl-lane-1); }

/* ── THE PHONE'S CALL BAR (Rossi, 2 Oct: the vertical list stays, the call sits at its foot) ──────
   Sticky at the foot of the list, in the board's dark blue like the desk's call panel, so a call is
   never mistaken for part of the timeline. One line: who is speaking, about what, and the microphone;
   the board's own bottom bar carries Hang up and the clock (on the timeline's own page they are here). */
.tl-callbar { position: sticky; bottom: 0; z-index: 6; margin: 0 -14px; padding: 8px 14px calc(8px + env(safe-area-inset-bottom)); display: flex; align-items: center; gap: 10px; background: var(--navy); color: var(--on-navy); }
.tl-cb-face { flex: 0 0 auto; width: 30px; height: 30px; display: inline-flex; align-items: center; justify-content: center; border-radius: 50%; background: var(--navy-tag); overflow: hidden; }
.tl-cb-face .tl-call-av { width: 30px; height: 30px; display: block; }
.tl-cb-face img { width: 30px; height: 30px; border-radius: 50%; object-fit: contain; display: block; }
.tl-cb-say { flex: 1 1 auto; min-width: 0; margin: 0; font-size: 13px; font-weight: 600; line-height: 1.25; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
/* the bar saying WHY a call did not start: a message, not a control, so it is quieter and has nothing to press
   (Rossi, 2 Oct: a refused call had nowhere to appear on a phone and read as a dead button) */
/* the phone's timeline has no side padding of its own (each part pads itself), so the bar's -14px bleed pushed it 14px
   past the screen: the whole page scrolled sideways and zoomed out during every call, and a post's sheet then ran off
   the bottom (14 Oct, found checking Rossi's "the call button was not the red hang up" on a 390px phone) */
.tl-app.vertical > .tl-callbar { margin: 0; }
.tl-callbar.saying .tl-cb-say { font-weight: 500; -webkit-line-clamp: 3; }
.tl-callbar.saying .tl-cb-face { opacity: .75; }
.tl-cb-btn { flex-shrink: 0; min-width: 44px; min-height: 44px; display: inline-flex; align-items: center; justify-content: center; gap: 6px; padding: 0 12px; border-radius: 999px; border: 1px solid var(--navy-rule2); background: transparent; color: inherit; font-size: 13.5px; font-weight: 700; }
.tl-cb-btn[aria-pressed="true"] { background: var(--navy-tag); border-color: transparent; }
.tl-cb-btn.hang { border-color: transparent; background: var(--tl-hang); color: var(--tl-on-hang); }
.tl-cb-btn:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: 2px; }

/* the play bar stays at the bottom; while playing, the caption bar takes its place */
.tl-vbar { position: sticky; bottom: 0; z-index: 5; padding: 0 14px 10px; background: var(--tl-card); border-top: 1px solid var(--tl-rule); }
.tl-vprog { height: 3px; margin: 0 -14px 8px; background: var(--tl-rule); }
.tl-vprog div { height: 3px; width: 0; background: var(--tl-play); }
.tl-vplay { all: unset; box-sizing: border-box; display: flex; align-items: center; gap: 12px; min-height: 48px; cursor: pointer; color: var(--tl-ink); }
.tl-vplay[disabled] { opacity: .5; cursor: default; }
.tl-vplay-ic { width: 48px; height: 48px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; border-radius: 50%; background: var(--tl-btn-bg); color: var(--tl-btn-fg); }
.tl-vplay-t { display: flex; flex-direction: column; line-height: 1.25; }
.tl-vplay-t b { font-size: 15px; }
.tl-vplay-t span { font-size: 12.5px; color: var(--tl-ink2); }
.tl-app.vertical .tl-player { position: sticky; bottom: 0; z-index: 6; padding: 0 14px 10px; }
.tl-app.vertical .tl-prog { margin: 0 -14px 8px; }
.tl-app.vertical .tl-prow { flex-direction: column; align-items: stretch; gap: 8px; }
.tl-app.vertical .tl-captext { font-size: 15px; }
.tl-app.vertical .tl-pctl { flex-wrap: wrap; gap: 6px; }
.tl-app.vertical .tl-spd-label { display: none; }
.tl-app.vertical .tl-foot { padding: 10px 14px 14px; }
.tl-app.vertical.playing .tl-controls { display: none; }

/* the card sheet, attached to the page itself (outside every board panel) */
.tl-portal { position: relative; z-index: 2100; font-family: 'DM Sans', sans-serif; font-size: 14px; color: var(--tl-ink); -webkit-font-smoothing: antialiased; }
.tl-portal *, .tl-portal *::before, .tl-portal *::after { box-sizing: border-box; }
.tl-scrim { position: fixed; inset: 0; z-index: 2100; background: var(--tl-ink); opacity: .42; }
/* every card opens at 95% of the screen (Rossi, 29 Oct: "also open this panel to 95% of the vertical screen to stay
   consistent"), in svh so a keyboard never shrinks it (the viewport's interactive-widget=resizes-visual) */
.tl-sheet { position: fixed; left: 0; right: 0; bottom: 0; z-index: 2101; height: 95vh; height: 95svh; max-height: 95svh; display: flex; flex-direction: column; overflow: hidden; overscroll-behavior: contain; padding: 8px 0 0; background: var(--tl-card); border-radius: 16px 16px 0 0; box-shadow: 0 -8px 32px var(--tl-shadow); transition: transform .2s ease; outline: none; }
.tl-sheet-grab { width: 40px; height: 4px; margin: 2px auto 10px; border-radius: 2px; background: var(--tl-rule2); }
/* a solid round button, so it stays readable over a dark picture */
.tl-sheet-x { all: unset; position: absolute; top: 10px; right: 10px; z-index: 1; width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; border-radius: 22px; background: var(--tl-card); border: 1px solid var(--tl-rule); box-shadow: 0 2px 8px var(--tl-shadow); cursor: pointer; color: var(--tl-ink); }
.tl-sheet-x:focus-visible, .tl-sheet-open:focus-visible, .tl-sheet-play:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: 2px; }
.tl-sheet-media { position: relative; margin: 6px 0 12px; aspect-ratio: 16 / 9; overflow: hidden; border-radius: 10px; background: var(--tl-media); }
.tl-sheet-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* A CARD WITH A PICTURE OPENS AT 95% OF THE SCREEN, THE PICTURE TAKING WHAT THE WORDS LEAVE (Rossi, 8 Oct). The sheet
   is a column: the grab bar, the picture, the words (which scroll inside, so the sheet itself never does), and the
   call row pinned at the foot. Measured on a 844px phone: 802px of sheet, about 100px of call row, 16px of grab and
   roughly 260px of words leave the picture about 430px, over half the panel and nearly two thirds of what is not the
   call. A card with NO picture is left as it was, sized to what it holds, so a short one is not a tall empty box. */
.tl-sheet.tall { height: 95vh; height: 95svh; max-height: 95svh; display: flex; flex-direction: column; overflow: hidden; padding: 8px 0 0; }
.tl-sheet.tall .tl-sheet-grab { flex: none; }
.tl-sheet.tall .tl-sheet-media { flex: 1 1 auto; min-height: 30dvh; margin: 6px 18px 0; aspect-ratio: auto; background: var(--tl-pv-bg); }
/* THE WHOLE PICTURE, NOT THE MIDDLE OF IT. A tall box cropped a wide picture down its own sides: measured on a 638 by
   360 post, a 354 by 410 box cut about 45% of the width away and took half the words with it ("limited text chats w").
   So the picture sits whole, as it does in the viewer the magnifying glass opens, on a blurred copy of itself rather
   than on two empty bands: the box is full at every shape, and nothing of the post is hidden. */
.tl-sheet.tall .tl-sheet-media > img { position: absolute; inset: 0; object-fit: contain; }
.tl-sheet-blur { display: none; }
.tl-sheet.tall .tl-sheet-media > img.tl-sheet-blur { display: block; inset: -10%; width: 120%; height: 120%; object-fit: cover; filter: blur(24px) saturate(1.3) brightness(.78); }
/* 24px at the foot, not 18: the words scroll inside, and at rest the last line was left 4px clear of the call row on a
   360px phone, which reads as a button cut off rather than a list that scrolls (design critique, 8 Oct) */
/* every sheet is a column (29 Oct, with the 95% height): the words scroll in the middle, the call row stays pinned at the foot */
.tl-sheet .tl-sheet-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 0 18px 24px; }
.tl-sheet.tall .tl-sheet-body { flex: 0 1 auto; }
.tl-sheet .tl-sheet-callrow { flex: none; margin: 0; }
/* a board with calls off has no row at the foot, so the words carry the bottom gap themselves */
.tl-sheet .tl-sheet-body:last-child { padding-bottom: calc(18px + env(safe-area-inset-bottom)); }
/* the rhythm between the words, set once here rather than one margin per element: the date and the title belong
   together, the figures and the buttons each want their own air (design critique, 8 Oct) */
.tl-sheet-body > .tl-sheet-meta { margin-top: 14px; }
.tl-sheet-body > .tl-sheet-date { margin-top: 10px; }
.tl-sheet-body > .tl-sheet-t { margin-top: 5px; }
.tl-sheet-body > .tl-sheet-sum { margin-top: 10px; }
.tl-sheet-body > .tl-sheet-stats { margin-top: 18px; }
.tl-sheet-body > .tl-sheet-news, .tl-sheet-body > .tl-sheet-fol { margin-top: 18px; }
.tl-sheet-body > .tl-sheet-acts { margin-top: 20px; }
.tl-sheet-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding-right: 44px; font-size: 13.5px; font-weight: 600; }
.tl-sheet-date { margin-top: 6px; font-size: 13px; color: var(--tl-ink2); }
.tl-sheet-t { margin: 6px 0 0; font-size: 20px; font-weight: 700; line-height: 1.25; color: var(--tl-ink); }
.tl-sheet-sum { margin: 8px 0 0; font-size: 14.5px; line-height: 1.45; color: var(--tl-ink2); }
/* a school menu's week, one row a day (Rossi, 29 Oct: "enforce 1 row per day for each daily respective menu"): the day
   in its own column, the main dish beside it, the meat-free note under the dish in the kind's petrol blue */
.tl-menu-days { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 6px; }
.tl-menu-days li { display: grid; grid-template-columns: 58px minmax(0, 1fr); gap: 10px; align-items: start; padding: 9px 12px; border-radius: 10px;
  background: var(--tl-canvas); border: 1px solid var(--tl-rule); font-size: 14.5px; line-height: 1.4; color: var(--tl-ink); }
.tl-menu-days li b { font-weight: 700; color: var(--tl-ink2); white-space: nowrap; }
.tl-menu-days li span { min-width: 0; }
.tl-menu-days li i { display: block; margin-top: 2px; font-style: normal; font-size: 12.5px; font-weight: 600; color: #1F7A8C; }
.tl-call-sum.tl-menu-days { margin-top: 8px; }
/* (30 Oct) the dark blue panel turns the words white but kept the rows' light canvas, so a computer's opened week showed
   white words on white rows (seen in the English menus' review); the rows take the panel's own shade, as the deals do */
.tl-call.is-navy .tl-menu-days li { background: var(--navy-2, rgba(255, 255, 255, .07)); }
.tl-call.is-navy .tl-menu-days li i { color: #8FD3E0; }
/* (29 Oct) a supermarket's week, one row a product, as the school menus (Rossi: "restructure ... so it is more visually
   readable"): the product, with its size and price under it, and the cut on the right in a badge of the kind's red */
.tl-deal-list { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 6px; }
.tl-deal-list li { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 12px; align-items: center; padding: 9px 12px; border-radius: 10px; background: var(--tl-tag); font-size: 14px; line-height: 1.35; color: var(--tl-ink); }
.tl-deal-n { min-width: 0; font-weight: 600; }
.tl-deal-n i { display: block; margin-top: 2px; font-style: normal; font-size: 12.5px; font-weight: 500; color: var(--tl-ink2); }
.tl-deal-cut { flex: none; padding: 3px 9px; border-radius: 999px; background: #B3263E; color: #fff; font-size: 13px; font-weight: 800; white-space: nowrap; font-variant-numeric: tabular-nums; }
.tl-deal-more { margin: 8px 2px 0; font-size: 13px; color: var(--tl-ink2); }
.tl-call-sum.tl-deal-list { margin-top: 8px; }
.tl-call .tl-deal-list li { background: var(--navy-2, rgba(255, 255, 255, .07)); color: var(--on-navy); }
.tl-call .tl-deal-n i, .tl-call .tl-deal-more { color: var(--on-navy-2, rgba(255, 255, 255, .72)); }
/* a product's picture in the strip carries its name and cut, so the strip says what each picture is */
.tl-gal-fig { position: relative; flex: none; width: 100%; height: 100%; margin: 0; scroll-snap-align: start; scroll-snap-stop: always; background: #fff; }
.tl-gal-fig img { width: 100%; height: 100%; object-fit: contain; background: #fff; }
.tl-gal-fig figcaption { position: absolute; left: 10px; right: 10px; bottom: 28px; display: flex; align-items: center; justify-content: center; gap: 8px; padding: 5px 6px 5px 10px; border-radius: 8px; background: rgba(13, 27, 52, .84); color: #fff; font-size: 12.5px; font-weight: 600; line-height: 1.3; }
.tl-gal-fig figcaption span { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tl-gal-fig figcaption b { flex: none; padding: 1px 7px; border-radius: 999px; background: #B3263E; font-weight: 800; white-space: nowrap; }
/* on a product's white packshot the white dots vanished: navy dots there */
.tl-gal:has(.tl-gal-fig) .tl-gal-dots i { background: rgba(13, 27, 52, .28); box-shadow: none; }
.tl-gal:has(.tl-gal-fig) .tl-gal-dots i.on { background: #0D1B34; }
/* the computer's card panel: the same strip, in the panel's picture frame */
.tl-call-media.gal { position: relative; aspect-ratio: 4 / 3; }
.tl-call-media.gal .tl-gal { position: absolute; inset: 0; }
.tl-call-media.gal .tl-gal-strip img { object-fit: contain; }
/* a home's photos, swiped sideways (29 Oct): a scroll-snap strip filling the picture box, dots and a count over it */
.tl-gal { position: absolute; inset: 0; }
.tl-sheet:not(.tall) .tl-sheet-media .tl-gal, .tl-fs-media.gal { position: relative; inset: auto; }
.tl-sheet:not(.tall) .tl-sheet-media .tl-gal { aspect-ratio: 16 / 9; }
.tl-fs-media.gal { aspect-ratio: 16 / 9; }
.tl-gal-strip { display: flex; width: 100%; height: 100%; overflow-x: auto; overflow-y: hidden; scroll-snap-type: x mandatory; scrollbar-width: none; -webkit-overflow-scrolling: touch; overscroll-behavior-x: contain; }
.tl-gal-strip::-webkit-scrollbar { display: none; }
.tl-gal-strip img { flex: none; width: 100%; height: 100%; object-fit: cover; scroll-snap-align: start; scroll-snap-stop: always; display: block; background: var(--tl-media); }
.tl-sheet.tall .tl-gal-strip img { object-fit: contain; background: var(--tl-pv-bg); }
.tl-gal-dots { position: absolute; left: 0; right: 0; bottom: 10px; display: flex; justify-content: center; gap: 5px; pointer-events: none; }
.tl-gal-dots i { width: 6px; height: 6px; border-radius: 50%; background: rgba(255, 255, 255, .55); box-shadow: 0 0 0 1px rgba(0, 0, 0, .25); transition: transform .15s, background .15s; }
.tl-gal-dots i.on { background: #fff; transform: scale(1.35); }
.tl-gal-n { position: absolute; top: 10px; left: 10px; padding: 3px 8px; border-radius: 999px; background: rgba(0, 0, 0, .55); color: #fff; font-size: 12px; font-weight: 700; letter-spacing: .02em; pointer-events: none; }
.tl-gal:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: -2px; }
/* (29 Oct) the arrows a mouse needs (Rossi: "Add discret but visible arrows"): round, frosted navy, at mid height, clear
   of the count and the magnifying glass; a little stronger on hover; gone at either end. A phone swipes, so they show only
   where there is a mouse or a wider screen. A click on either outer third of the picture steps too (timeline.js) */
.tl-gal-arr { all: unset; box-sizing: border-box; position: absolute; top: 50%; z-index: 2; display: none; align-items: center; justify-content: center; width: 34px; height: 34px; margin-top: -17px; border-radius: 50%; background: rgba(12, 29, 58, .55); color: #fff; box-shadow: 0 1px 4px rgba(0, 0, 0, .25); -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px); cursor: pointer; transition: background .15s, opacity .15s; }
.tl-gal-arr.prev { left: 10px; }
.tl-gal-arr.next { right: 10px; }
.tl-gal-arr svg { width: 18px; height: 18px; }
.tl-gal-arr:hover { background: rgba(12, 29, 58, .82); }
.tl-gal-arr:focus-visible { outline: 2px solid var(--gv-accent, #3AD0EC); outline-offset: 2px; }
.tl-gal-arr[disabled] { opacity: 0; pointer-events: none; }
@media (hover: hover) and (pointer: fine), (min-width: 700px) { .tl-gal-arr { display: inline-flex; } }
@media (hover: hover) and (pointer: fine), (min-width: 700px) { .tl-gal-strip { cursor: pointer; } }
.tl-sheet-stats { display: flex; flex-wrap: wrap; gap: 6px 16px; margin: 14px 0 0; padding: 0; list-style: none; font-size: 13.5px; color: var(--tl-ink2); }
.tl-sheet-stats b { color: var(--tl-ink); font-variant-numeric: tabular-nums; }
.tl-sheet-acts { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 18px; }
.tl-sheet-open, .tl-sheet-play { flex: 1 1 auto; min-height: 48px; padding: 0 18px; display: inline-flex; align-items: center; justify-content: center; gap: 8px; border-radius: 999px; font: inherit; font-size: 14.5px; font-weight: 600; text-decoration: none; cursor: pointer; }
.tl-sheet-open { border: 1px solid var(--tl-rule2); background: var(--tl-card); color: var(--tl-ink); }
.tl-sheet-play { border: 0; background: var(--tl-btn-bg); color: var(--tl-btn-fg); }
/* Play the story, parked (Rossi, 1 Oct: "remove/hide the play the story sticky row at the bottom of all devices"): the
   bottom bar and the wide view's button stay hidden whatever the script sets (it is also hidden at each of its setters,
   so its height measures 0 for every scroll aim) */
.tl-app.no-story .tl-vbar, .tl-app.no-story .tl-playbtn, .tl-app.no-story .tl-player { display: none !important; }
/* (the narrow wide view gave the button its own column: without it, the platform menu takes the row) */
@media (max-width: 640px) { .tl-app.no-story .tl-right { grid-template-areas: "plat plat" "zoom zoom"; } }
/* the call (Gate C): the week's new followers above the actions, and one calm call row under them ("Play from here"
   stays the one filled button) */
.tl-sheet-fol { margin: 14px 0 0; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; font-size: 13px; line-height: 1.5; color: var(--tl-ink2); }
.tl-sheet-fol b { display: inline-flex; align-items: center; gap: 5px; color: var(--tl-ink); font-variant-numeric: tabular-nums; }
.tl-sheet-fol .tl-sw.logo { width: 16px; height: 16px; }
/* the card's call row wears the board's dark blue, as the desk's call panel does (Rossi, 2 Oct: "make it look like one
   the desktop version"), so the one green button sits on navy on every device */
.tl-sheet-callrow { margin: 16px -18px calc(-18px - env(safe-area-inset-bottom)); padding: 14px 18px calc(18px + env(safe-area-inset-bottom)); border-top: 0; background: var(--navy); color: var(--on-navy); }
.tl-sheet-callrow .tl-call-who { color: var(--on-navy); }
/* "Talk it through" in the board's gold (Rossi, 2 Oct: "change the font color to the tracketing theme yellow as this
   color is barely visible"). The row went navy on 2 Oct and this heading kept the light card's dark ink, which reads
   at 1.7 to 1 on navy. The gold is 5.7 to 1 and is the accent the navy call panel already uses for its buttons. */
.tl-sheet-callrow .tl-call-id b { color: var(--gold); }
/* the row saying calls are off: the same navy, no button, so it reads as a notice and not a dead control */
.tl-sheet-callrow.quiet .tl-call-id b { color: var(--on-navy); }
.tl-sheet-callrow.quiet .tl-call-who { align-items: flex-start; }
.tl-sheet-callrow .tl-call-id span { color: var(--on-navy-2); }
.tl-sheet-callrow .tl-call-btn { height: 48px; }
/* on a call, the sheet's Hang up is the filled red one of the phone's call bar (14 Oct, Rossi: "the red hangup button I was expecting") */
.tl-sheet-callrow .tl-call-btn.hang { background: var(--tl-hang) !important; border-color: transparent !important; color: var(--tl-on-hang) !important; }

/* ── the full-screen swipe player on phones (handoff §7, Gate C) ────────────────────────────────
   One moment per screen, snapping natively; attached to the page (a .tl-portal) so no board panel
   draws over it. 48px controls, safe-area padding for the iPhone home indicator. */
html.tl-fs-open, html.tl-fs-open body { overflow: hidden; }
.tl-fs-wrap { position: fixed; inset: 0; z-index: 2200; }
.tl-fs { position: absolute; inset: 0; display: flex; flex-direction: column; height: 100%; background: var(--tl-canvas); color: var(--tl-ink); outline: none; }
/* only the screens give way when the phone is short: the bars above and below keep their size */
.tl-fs-top, .tl-fs-strip, .tl-fs-dates, .tl-fs-dock { flex-shrink: 0; }
.tl-fs-top { display: flex; align-items: center; gap: 4px; min-height: 52px; padding: 4px 16px 0 4px; padding-top: max(4px, env(safe-area-inset-top)); }
.tl-fs-x { all: unset; box-sizing: border-box; width: 44px; height: 44px; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; border-radius: 22px; cursor: pointer; color: var(--tl-ink); }
.tl-fs-title { flex: 1; min-width: 0; display: flex; align-items: baseline; justify-content: space-between; gap: 8px; line-height: 1.25; }
.tl-fs-title b { min-width: 0; font-size: 15px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tl-fs-count { flex-shrink: 0; font-size: 12.5px; font-weight: 500; color: var(--tl-ink2); font-variant-numeric: tabular-nums; white-space: nowrap; }
.tl-fs-strip { position: relative; height: 18px; margin: 4px 16px 0; }
.tl-fs-line { position: absolute; left: 0; right: 0; top: 8.5px; height: 1px; background: var(--tl-rule2); }
.tl-fs-dot { position: absolute; width: 5px; height: 5px; margin-left: -2.5px; border-radius: 50%; }
.tl-fs-dot.l0 { top: 2px; background: var(--tl-lane-0); }
.tl-fs-dot.l1 { top: 11px; background: var(--tl-lane-1); }
.tl-fs-dot.ms { background: var(--tl-canvas); box-shadow: inset 0 0 0 1.5px var(--tl-ink3); }
.tl-fs-head { position: absolute; top: -1px; width: 2px; height: 20px; margin-left: -1px; border-radius: 1px; background: var(--tl-play); transition: left .25s ease; }
.tl-fs-dates { position: relative; display: flex; justify-content: space-between; margin: 2px 16px 0; font-size: 11.5px; font-weight: 500; color: var(--tl-ink2); }
.tl-fs-now { position: absolute; top: 0; transform: translateX(-50%); padding: 0 4px; background: var(--tl-canvas); font-weight: 700; color: var(--tl-ink); white-space: nowrap; transition: left .25s ease; }
.tl-fs-screens { position: relative; flex: 1 1 auto; min-height: 0; overflow-y: auto; scroll-snap-type: y mandatory; overscroll-behavior: contain; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
.tl-fs-screens::-webkit-scrollbar { display: none; }
.tl-fs-screen { height: 100%; scroll-snap-align: start; scroll-snap-stop: always; padding: 10px 12px 0; display: flex; flex-direction: column; justify-content: flex-start; }
.tl-fs-card { flex: 0 1 auto; display: flex; flex-direction: column; gap: 8px; max-height: 100%; overflow: hidden; padding: 16px; border-radius: 16px; border: 1px solid var(--tl-rule); border-top-width: 3px; background: var(--tl-card); cursor: pointer; }
.tl-fs-card.l0 { border-top-color: var(--tl-lane-0); }
.tl-fs-card.l1 { border-top-color: var(--tl-lane-1); }
.tl-fs-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; font-size: 14px; font-weight: 600; }
.tl-fs-date { font-size: 13px; color: var(--tl-ink2); }
.tl-fs-h { margin: 2px 0 0; font-size: 23px; line-height: 1.18; font-weight: 700; letter-spacing: -0.01em; }
.tl-fs-sum { margin: 0; font-size: 15px; line-height: 1.4; color: var(--tl-ink2); }
/* a picture keeps a readable height on a small phone (review round 2, 29 Sep: 30 of 34 were a 14px strip at 360x640);
   the summary gives up lines first, and the picture is fitted, never cropped (spec 1.3) */
.tl-fs-media { flex-shrink: 1; min-height: min(88px, 14vh); overflow: hidden; border-radius: 10px; background: var(--tl-media); }
.tl-fs-media img { display: block; width: 100%; height: 100%; max-height: 30vh; max-height: 30dvh; object-fit: contain; }
.tl-fs-sum { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 4; overflow: hidden; }
/* the numbers as the panel shows them: each figure over its word, in columns */
.tl-fs-stats { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 10px; margin: 4px 0 0; padding: 0; list-style: none; font-size: 13px; line-height: 1.3; color: var(--tl-ink2); }
.tl-fs-stats li { display: flex; flex-direction: column; min-width: 0; }
.tl-fs-stats b { font-size: 18px; font-weight: 700; color: var(--tl-ink); font-variant-numeric: tabular-nums; white-space: nowrap; }
/* a.tl-fs-open, never .tl-fs-open alone: <html> carries that class while the player is open (the scroll lock), and
   a bare rule styled the whole page (it turned every icon in the player grey, build check, 29 Sep) */
a.tl-fs-open { align-self: flex-start; min-height: 44px; display: inline-flex; align-items: center; gap: 6px; font-size: 14.5px; font-weight: 600; color: var(--tl-ink); text-decoration: underline; text-underline-offset: 3px; }
a.tl-fs-open svg { flex-shrink: 0; color: var(--tl-ink2); }
/* the next moment, peeking under the card (only where 48px or more are free: .off) */
.tl-fs-peek { flex: 1 1 0; min-height: 0; margin-top: 10px; padding: 11px 16px 0; overflow: hidden; background: var(--tl-card); border: 1px solid var(--tl-rule); border-bottom: 0; border-top: 3px solid var(--tl-lane-0); border-radius: 16px 16px 0 0; cursor: pointer; }
.tl-fs-peek.l1 { border-top-color: var(--tl-lane-1); }
.tl-fs-peek.off { visibility: hidden; }
.tl-fs-pk { display: flex; align-items: center; gap: 8px; font-size: 12.5px; font-weight: 600; color: var(--tl-ink2); }
.tl-fs-pk .tl-sw.logo { width: 20px; height: 20px; }
.tl-fs-pkt { margin-top: 5px; font-size: 15px; font-weight: 700; line-height: 1.25; color: var(--tl-ink2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tl-fs-end { justify-content: center; text-align: center; }
.tl-fs-endcard { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 0 12px; }
.tl-fs-endpair { display: flex; gap: 6px; }
.tl-fs-endpair .tl-sw.logo { width: 36px; height: 36px; }
.tl-fs-endcard h2 { margin: 6px 0 0; font-size: 26px; line-height: 1.15; font-weight: 700; }
.tl-fs-endcard p { margin: 0; font-size: 15px; line-height: 1.45; color: var(--tl-ink2); }
.tl-fs-endsub { color: var(--tl-ink2) !important; }   /* on the player's grey: ink3 was 4.17:1 */
.tl-fs-again { margin-top: 10px; min-height: 48px; padding: 0 20px; display: inline-flex; align-items: center; gap: 8px; border: 0; border-radius: 999px; background: var(--tl-btn-bg); color: var(--tl-btn-fg); font: inherit; font-weight: 600; cursor: pointer; }
/* under the screens: the note, the words when read aloud, the week's followers, the call's slot, the controls */
.tl-fs-dock { display: flex; flex-direction: column; gap: 10px; padding: 10px 12px 12px; padding-bottom: max(12px, env(safe-area-inset-bottom)); background: var(--tl-canvas); }
.tl-fs-note { margin: 0; padding: 0 4px; font-size: 13.5px; line-height: 1.45; color: var(--tl-ink); }
.tl-fs-note[hidden] { display: none; }
/* the narration stays the polite live region; its words are seen only while read aloud (or in a recording), where
   they are one height for every moment, so the screens never resize */
.tl-fs:not(.cap-on) .tl-fs-cap { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
.tl-fs-cap { padding: 0 4px; height: 66px; overflow: hidden; }
.tl-fs-capmeta { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
.tl-fs-captext { font-size: 15px; line-height: 1.45; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.tl-fs-fol { display: flex; align-items: center; gap: 10px; padding: 0 4px; font-size: 12.5px; color: var(--tl-ink2); font-variant-numeric: tabular-nums; }
.tl-fs-fl { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tl-fs-fol b { display: inline-flex; align-items: center; gap: 5px; color: var(--tl-ink); font-weight: 700; }
.tl-fs-fol .tl-sw.logo { width: 18px; height: 18px; }
.tl-fs-ctl { display: flex; align-items: center; justify-content: flex-start; gap: 8px; }
.tl-fs-speed { margin-left: auto; }
.tl-fs-ctl button { min-width: 48px; min-height: 48px; padding: 0 14px; display: inline-flex; align-items: center; justify-content: center; border-radius: 24px; border: 1px solid var(--tl-rule); background: var(--tl-card); color: var(--tl-ink); font: inherit; font-size: 14px; font-weight: 600; cursor: pointer; }
.tl-fs-ctl .main { width: 56px; height: 56px; border: 0; border-radius: 28px; background: var(--tl-btn-bg); color: var(--tl-btn-fg); }
.tl-fs-voice[aria-pressed="true"] { background: var(--tl-btn-bg); color: var(--tl-btn-fg); }
.tl-fs-x:focus-visible, .tl-fs-ctl button:focus-visible, a.tl-fs-open:focus-visible, .tl-fs-again:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: 2px; }
/* the 9:16 recording: the player in a 9:16 frame with the board's ink around it, and a larger caption */
.tl-fs-wrap.present { background: var(--tl-ink); }
.tl-fs-wrap.present .tl-fs { left: 50%; right: auto; width: min(100vw, calc(100vh * 9 / 16)); transform: translateX(-50%); }
.tl-fs-wrap.present .tl-fs-cap { height: 104px; }
.tl-fs-wrap.present .tl-fs-captext { font-size: clamp(16px, 2.4vh, 22px); -webkit-line-clamp: 4; font-weight: 600; }
.tl-fs-wrap.present .tl-fs-h { font-size: clamp(24px, 3.2vh, 34px); }
/* the 9:16 recording: the picture is held to 18vh so the card fits the frame, and there is no link (nothing to click)
   (review round 2, 29 Sep: pictures were slivers, 13 cards were cut, and every card showed "Open the post") */
.tl-fs-wrap.present .tl-fs-media { flex-shrink: 1; min-height: 12vh; }
.tl-fs-wrap.present .tl-fs-media img { max-height: 18vh; }
.tl-fs-wrap.present .tl-fs-sum { -webkit-line-clamp: 3; }
.tl-fs-wrap.present a.tl-fs-open { display: none; }
@media (prefers-reduced-motion: reduce) { .tl-fs-head { transition: none; } .tl-fs-screens { scroll-behavior: auto; } }

/* ── presentation mode (§8): the chrome goes, the view locks to 16:9, the caption grows ─────── */
/* The frame sits OUTSIDE .tl-app, where the --tl-* roles are not defined, so it reads the board
   variable directly: the letterbox around a 16:9 recording is the board's ink. */
.tl-present-frame { position: fixed; inset: 0; display: flex; align-items: center; justify-content: center; background: var(--text-primary); }
.tl-present-frame .tl-app { aspect-ratio: 16 / 9; width: min(100vw, calc(100vh * 16 / 9)); height: auto; max-height: 100vh; }
.tl-app.present .tl-foot { display: none; }
.tl-app.present .tl-captext { font-size: clamp(20px, 2.2vw, 30px); line-height: 1.3; font-weight: 600; }
.tl-app.present .tl-capmeta { font-size: clamp(13px, 1.1vw, 16px); color: var(--tl-ink2); }
.tl-app.present .tl-controls .tl-chips, .tl-app.present .tl-platwrap, .tl-app.present .tl-icon { display: none; }

@media (prefers-reduced-motion: reduce) {
  .tl-sheet { transition: none; }
  .tl-prog div { transition: none; }
  .tl-spinner { animation: none; }
}

/* ══ THE CALL (Gate C, 29 Sep; the final design: DESIGN_SPEC, moment-first with grafts) ═════════════════════════
   Rossi: "replace the right hand panel with more information of the card being discussed and the call related
   buttons (call, hang up, call real time transcript ...)". The panel is the card, opened: the same chips and logo
   rings, on the board's dark blue in the right panel and the drawer (is-navy, below) and on the light card on a
   phone. One calm contact row at its foot: the Tracketing mark as the AI analyst's face, what it does in words, one
   button. Its boldness is spent in one place, the words of a live call. Every colour is a board variable (build rule
   T1); nothing here styles #right-panel (the Companies list and drawer keep theirs). */

/* ── the drawer: the same panel where the timeline is wide but has no right slot (the standalone page, and the board
   from 672 to 768px). timeline.js sets its top to the drawing area's top and its bottom to the host's inset. ── */
.tl-call-drawer { position: fixed; z-index: 40; right: 0; top: var(--tl-drawer-top, 0px); bottom: var(--tl-drawer-bottom, 0px); width: clamp(320px, 25vw, 360px); overflow: hidden; background: var(--bg-card); border-top: 1px solid var(--border); border-left: 1px solid var(--border); border-top-left-radius: 12px; box-shadow: -12px 0 32px var(--shadow); animation: tl-drawer-in .2s ease-out; }
.tl-call-drawer[hidden] { display: none; }
@keyframes tl-drawer-in { from { opacity: 0; transform: translateX(16px); } }
/* while it is open, the timeline's footer and story bar end at its left edge, so no control of theirs is under it */
.tl-app.drawer-open .tl-foot, .tl-app.drawer-open .tl-player { margin-right: var(--tl-drawer-cover, 0px); }
.tl-app.drawer-open .tl-player { container-type: inline-size; }
@container (max-width: 720px) { .tl-prow { flex-direction: column; align-items: stretch; gap: 8px; } .tl-pctl { flex-wrap: wrap; gap: 6px; } }
.tl-call-close { position: absolute; top: 10px; right: 10px; z-index: 2; width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; border-radius: 22px; background: var(--tl-card); border: 1px solid var(--tl-rule) !important; box-shadow: 0 2px 8px var(--tl-shadow); color: var(--tl-ink); }
.tl-call.in-call .tl-call-close { display: none; }

/* ── the panel (fills its host: the board's right slot, or the drawer) ── */
.tl-call { position: relative; height: 100%; min-height: 0; display: flex; flex-direction: column; background: var(--tl-card); color: var(--tl-ink); font-family: 'DM Sans', sans-serif; font-size: 14px; line-height: 1.4; text-align: left; -webkit-font-smoothing: antialiased; }
:where(.tl-call, .tl-sheet-callrow, .tl-fs-call) *, :where(.tl-call, .tl-sheet-callrow, .tl-fs-call) *::before, :where(.tl-call, .tl-sheet-callrow, .tl-fs-call) *::after { box-sizing: border-box; }
/* zero specificity on purpose: a reset must never beat a component's own margin */
:where(.tl-call, .tl-sheet-callrow, .tl-fs-call) :where(p, h2, h3, h4, ul, ol, li, figure) { margin: 0; padding: 0; list-style: none; }
:where(.tl-call, .tl-sheet-callrow, .tl-fs-call) :where(button) { font: inherit; color: inherit; cursor: pointer; background: none; border: 0; padding: 0; }
:where(.tl-call, .tl-sheet-callrow, .tl-fs-call) :where(button, a, [tabindex]):focus-visible { outline: 2px solid var(--tl-focus); outline-offset: 2px; }
.tl-call [hidden] { display: none !important; }
.tl-call svg, .tl-sheet-callrow svg, .tl-fs-call svg { display: block; flex-shrink: 0; }

/* ── the dark blue (is-navy): the board's right panel and the drawer that slides in from the right, on the board and
   on the timeline's own page. Rossi, 29 Sep: the panel in "site theme color like the right hand panel on the default
   ai-board page", then "dark blue there too" for the drawer, so turning an iPad mid-call never changes the colour.
   The phone keeps the light card. White words, the tab's yellow for Call, the captions and the keyboard ring, a light
   blue for the link out, light green and red for rises and falls; every value is a board variable (T1), measured in
   board-shell.css. Defined once here, for every page that shows a call. ── */
.tl-call.is-navy, .tl-callbar.is-navy {
  --tl-card:     var(--navy);
  --tl-ink:      var(--on-navy);
  --tl-ink2:     var(--on-navy-2);
  --tl-ink3:     var(--on-navy-3);
  --tl-rule:     var(--navy-rule);
  --tl-rule2:    var(--navy-rule2);
  --tl-tag:      var(--navy-tag);
  --tl-media:    var(--navy-media);
  --tl-shadow:   var(--navy-shadow);
  --tl-btn-bg:   var(--gold);
  --tl-btn-fg:   var(--navy);
  --tl-focus:    var(--gold);
  --tl-pick-bg:  var(--navy);
  --tl-play:     var(--gold);
  --tl-up:       var(--navy-up);
  --tl-down:     var(--navy-down);
  --tl-live:     var(--navy-up);
  --tl-on-live:  var(--navy);
  --tl-hang:     var(--navy-down);
  --tl-on-hang:  var(--navy);
  --tl-alert:    var(--navy-down);
  --tl-lane-0:   var(--on-navy);
  --tl-lane-1:   var(--gold);
  --tl-call-green: var(--navy-call-green);
  scrollbar-color: var(--navy-thumb) transparent;   /* 3.8:1 on the navy; the rule2 outline was 2.6:1 */
}
/* "Call" is the one green button in the product (Rossi, 2 Oct: "the typical bright make a call green"): on the navy
   panel and everywhere else, white on #17803F (5.0:1), so it reads as a phone call and not as the story's yellow */
.tl-call-btn[data-cp="call"], .tl-sheet-callrow .tl-call-btn[data-a="call"], .tl-fs-call .tl-call-btn[data-a="call"] { background: var(--call-green-fill) !important; border-color: var(--call-green-fill) !important; color: var(--on-call-green) !important; }
.tl-call-btn[data-cp="call"]:hover, .tl-sheet-callrow .tl-call-btn[data-a="call"]:hover, .tl-fs-call .tl-call-btn[data-a="call"]:hover { filter: brightness(1.12); }
/* the way out to the post, in a light blue (Rossi, 29 Sep, reviewed with the design critique): it reads as a link,
   apart from the white words, the yellow of Call and the green of a rise; underlined, so colour is never the only
   sign; lighter under the mouse */
.tl-call.is-navy .tl-call-open, .tl-call.is-navy .tl-call-open svg { color: var(--navy-link); }
.tl-call.is-navy .tl-call-open { text-decoration-color: var(--navy-link-line); }
.tl-call.is-navy .tl-call-open:hover, .tl-call.is-navy .tl-call-open:hover svg { color: var(--navy-link-hover); text-decoration-color: var(--navy-link-hover); }
.tl-call.is-navy .tl-call-cap { color: var(--gold); }
/* the drawer's own frame follows: no grey edge round the dark blue, a deeper shadow on the light page beside it */
.tl-call-drawer.is-navy { background: var(--navy); border-color: var(--navy); box-shadow: -12px 0 32px var(--navy-shadow); }

.tl-call-main.more { -webkit-mask-image: linear-gradient(to bottom, var(--tl-ink) calc(100% - 32px), transparent 100%); mask-image: linear-gradient(to bottom, var(--tl-ink) calc(100% - 32px), transparent 100%); }
.tl-call-main { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 20px 20px 22px; scrollbar-width: thin; }
.tl-call.is-drawer .tl-call-main { padding-top: 62px; }

/* part 1, the moment: the card, opened */
.tl-call-follow { display: flex; align-items: center; gap: 7px; margin-bottom: 14px; font-size: 12.5px; font-weight: 600; color: var(--tl-ink2); }
.tl-call-follow svg { width: 12px; height: 12px; color: var(--tl-play); }
.tl-call-media { aspect-ratio: 16 / 9; margin-bottom: 16px; border: 1px solid var(--tl-rule); border-radius: 10px; overflow: hidden; background: var(--tl-media); }
.tl-call-media img { display: block; width: 100%; height: 100%; object-fit: contain; }
.tl-call-brand { display: flex; align-items: center; gap: 10px; min-height: 28px; }
/* every brand mark is the timeline's own swatch() (the logo in a ring of its lane colour; a failed logo leaves the
   plain lane square). Only sized here. */
.tl-call-brand .tl-sw.logo { width: 28px; height: 28px; }
.tl-call-hrow .tl-sw.logo, .tl-call-fol .tl-sw.logo, .tl-call-nb .tl-sw.logo, .tl-call-pick .tl-sw.logo { width: 20px; height: 20px; }
.tl-cl-mbtn .tl-sw.logo, .tl-sheet-fol .tl-sw.logo { width: 16px; height: 16px; }
.tl-call-fol .tl-sw, .tl-call-nb .tl-sw, .tl-call-pick .tl-sw { justify-self: center; }
.tl-call-bname { flex: 1; min-width: 0; font-size: 15px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tl-call-tag { flex-shrink: 0; padding: 2px 9px; border-radius: 999px; background: var(--tl-tag); font-size: 12px; font-weight: 500; line-height: 1.4; color: var(--tl-ink2); white-space: nowrap; }
.tl-call-date { margin-top: 10px; font-size: 13px; color: var(--tl-ink2); }
.tl-call-h { margin-top: 4px; font-size: 22px; line-height: 1.2; font-weight: 700; letter-spacing: -0.015em; color: var(--tl-ink); }
.tl-call-h:focus { outline: none; }
.tl-call-h:focus-visible, .tl-call-ht:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: 2px; border-radius: 4px; }
.tl-call-sum { margin-top: 6px; font-size: 14.5px; line-height: 1.45; color: var(--tl-ink2); }
.tl-call.is-story .tl-call-date { margin-top: 6px; }
.tl-call-stats { display: grid; grid-template-columns: repeat(4, auto); justify-content: space-between; column-gap: 12px; margin-top: 18px; }
.tl-call-stats li { min-width: 0; }
.tl-call-stats b { display: block; font-size: 17px; font-weight: 700; line-height: 1.2; font-variant-numeric: tabular-nums; color: var(--tl-ink); }
.tl-call-stats span { display: block; margin-top: 1px; font-size: 12px; color: var(--tl-ink3); }
.tl-call-acts { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 18px; margin-top: 12px; }
.tl-call-open { display: inline-flex; align-items: center; gap: 6px; min-height: 32px; font-size: 13.5px; font-weight: 600; color: var(--tl-ink); text-decoration: underline; text-decoration-color: var(--tl-rule2); text-underline-offset: 3px; }
.tl-call-open:hover { text-decoration-color: var(--tl-ink); }
.tl-call-open svg { color: var(--tl-ink2); }
.tl-call-playfrom { display: inline-flex; align-items: center; gap: 7px; min-height: 32px; padding: 0 13px 0 11px !important; border: 1px solid var(--tl-rule2) !important; border-radius: 999px; font-size: 13px; font-weight: 600; color: var(--tl-ink); }
.tl-call-playfrom svg { width: 11px; height: 11px; }
.tl-call-playfrom:hover { border-color: var(--tl-ink3) !important; }
.tl-call-sec { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--tl-rule); }
.tl-call-cap { font-size: 12px; font-weight: 500; color: var(--tl-ink3); }
.tl-call-fol { display: grid; grid-template-columns: 20px minmax(0, 1fr) auto 48px; column-gap: 8px; row-gap: 10px; align-items: center; margin-top: 10px; }
.tl-call-fn { min-width: 0; font-size: 14px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tl-call-fv { font-size: 14px; font-weight: 700; font-variant-numeric: tabular-nums; text-align: right; }
.tl-call-fc { font-size: 12.5px; font-weight: 500; font-variant-numeric: tabular-nums; text-align: right; color: var(--tl-up); }
.tl-call-fc.down { color: var(--tl-down); }
.tl-call-fnone { grid-column: 3 / 5; font-size: 12.5px; color: var(--tl-ink3); text-align: right; }
.tl-call-near { display: flex; flex-direction: column; gap: 2px; margin-top: 18px; padding-top: 8px; border-top: 1px solid var(--tl-rule); }
/* (29 Oct) what the two rows are (Rossi: "the user has no idea what this section is for") */
.tl-call-nearh { margin: 6px 0 4px; font-size: 11.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--tl-ink3); }
.tl-call-nbd { font-weight: 700; color: var(--tl-ink2); }
.tl-call-nb { display: grid; grid-template-columns: 16px 20px minmax(0, 1fr); align-items: center; column-gap: 9px; width: calc(100% + 16px); min-height: 52px; margin: 0 -8px; padding: 6px 8px !important; border-radius: 10px; text-align: left; }
.tl-call-nb:hover { background: var(--tl-tag); }
.tl-call-nbi { display: flex; color: var(--tl-ink3); }
.tl-call-nbt { min-width: 0; display: flex; flex-direction: column; }
.tl-call-nbm { font-size: 12px; color: var(--tl-ink3); }
.tl-call-nbh { font-size: 13.5px; font-weight: 600; color: var(--tl-ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* on the grey hover fill, grey meta text would fall to 4.17:1: it steps up to ink2 (8.1:1 on the fill) */
.tl-call-nb:hover .tl-call-nbm, .tl-call-nb:hover .tl-call-nbi, .tl-call-pick:hover .tl-call-pm { color: var(--tl-ink2); }

/* nothing picked yet */
.tl-call-eh { font-size: 20px; font-weight: 700; line-height: 1.25; letter-spacing: -0.01em; }
.tl-call-ep { margin-top: 8px; font-size: 13px; line-height: 1.55; color: var(--tl-ink2); }   /* 13px, one down (Rossi, 30 Sep) */
.tl-call-ep + .tl-call-ep { margin-top: 10px; }
.tl-call-glyph { display: inline-flex; width: 20px; height: 20px; margin: 0 2px; vertical-align: -5px; align-items: center; justify-content: center; border: 1px solid var(--tl-rule2); border-radius: 50%; background: var(--tl-card); color: var(--tl-call-green); }
.tl-call-glyph svg { display: inline-block; width: 12px; height: 12px; }
.tl-call-inview { margin-top: 24px; }
.tl-call-picks { margin-top: 8px !important; border: 1px solid var(--tl-rule); border-radius: 12px; overflow: hidden; }
.tl-call-picks li + li { border-top: 1px solid var(--tl-rule); }
.tl-call-pick { display: grid; grid-template-columns: 20px minmax(0, 1fr); column-gap: 10px; align-items: center; width: 100%; min-height: 56px; padding: 9px 12px !important; text-align: left; }
.tl-call-pick:hover { background: var(--tl-tag); }
.tl-call-pm { display: block; font-size: 12px; color: var(--tl-ink3); }
.tl-call-ph { display: block; font-size: 13.5px; font-weight: 600; color: var(--tl-ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tl-call-ep2 { margin-top: 18px; font-size: 13px; line-height: 1.5; color: var(--tl-ink3); }
.tl-call-ep2 b { font-weight: 600; color: var(--tl-ink2); }
.tl-call-ep + .tl-call-ep2, .tl-call-picks + .tl-call-ep2 { margin-top: 14px; }

/* during a call (and when the last call's words are open): the moment folded into a header */
.tl-call-head { flex-shrink: 0; padding: 14px 20px 13px; border-bottom: 1px solid var(--tl-rule); }
.tl-call.is-drawer .tl-call-head { padding-right: 20px; }
.tl-call.is-drawer:not(.in-call) .tl-call-head { padding-right: 64px; }   /* the close is back after a call: it never covers "Show details" */
/* the brand and the date are never cut: when they and "Show details" do not fit on one line, "Show details" moves
   under them (the design spec's check 7: "Anduril Industries, 15 Sep" in the tablet's 320px panel was cut short) */
.tl-call-hrow { display: flex; flex-wrap: wrap; align-items: center; column-gap: 8px; row-gap: 0; min-height: 30px; }
.tl-call-hmeta { flex: 0 1 auto; min-width: 0; font-size: 13px; font-weight: 600; color: var(--tl-ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tl-call-more { flex-shrink: 0; margin-left: auto; display: inline-flex; align-items: center; gap: 4px; height: 32px; margin-right: -8px; padding: 0 8px 0 10px !important; border-radius: 8px; font-size: 12.5px; font-weight: 600; color: var(--tl-ink2); }
.tl-call-more:hover { background: var(--tl-tag); color: var(--tl-ink); }
.tl-call-more svg, .tl-call-saidbtn svg { width: 15px; height: 15px; transition: transform .16s ease; }
.tl-call-more[aria-expanded="true"] svg { transform: rotate(180deg); }
.tl-call-ht { margin-top: 6px; font-size: 16px; line-height: 1.28; font-weight: 700; letter-spacing: -0.01em; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.tl-call-ht:focus { outline: none; }
.tl-call-hs { margin-top: 4px; font-size: 12.5px; line-height: 1.45; color: var(--tl-ink2); }
.tl-call-hs b { color: var(--tl-ink); font-weight: 600; font-variant-numeric: tabular-nums; }
.tl-call-head .tl-call-sum { margin-top: 8px; font-size: 13.5px; }
.tl-call-head .tl-call-stats { margin-top: 12px; }
.tl-call-head .tl-call-acts { margin-top: 8px; }
.tl-call-head .tl-call-sec { margin-top: 12px; padding-top: 12px; }
.tl-call-head.open { max-height: min(60%, calc(100% - 300px)); overflow-y: auto; }   /* the words and the dock always keep 300px */
/* (31 Oct) THE OPENED MOMENT BREATHES, on a computer and a tablet (Rossi, on a school-lunch week in the right panel: "the
   veritcal padding between these elements of this right hand panel shoudl be double ... Every elements look so densed and
   need more padding between them"). Measured before: 4 to 18px between blocks (10 place to date, 4 date to title, 6 title
   to summary, 12 summary to source, 18 to "Before and after", 2 between its rows). Each gap between two groups doubles; a
   pair that reads as one (the date over its title) stays the closest. The 20px side margin is kept, so the panel's left edge
   still lines up with the call bar under it. The phone's card sheet has its own rules and is unchanged. */
@media (min-width: 641px) {
  .tl-call-main { padding-top: 24px; padding-bottom: 32px; }
  .tl-call-moment > .tl-call-media { margin-bottom: 24px; }
  .tl-call-moment > .tl-call-date { margin-top: 20px; }
  .tl-call-moment > .tl-call-h { margin-top: 8px; }
  .tl-call-moment > .tl-call-sum { margin-top: 14px; }
  .tl-call-moment > .tl-call-sum.tl-menu-days, .tl-call-moment > .tl-call-sum.tl-deal-list { margin-top: 18px; gap: 10px; }
  .tl-call-moment > .tl-call-sum.tl-menu-days li, .tl-call-moment > .tl-call-sum.tl-deal-list li { padding-top: 11px; padding-bottom: 11px; }
  .tl-call-moment > .tl-deal-more { margin-top: 12px; }
  .tl-call-moment > .tl-call-news { margin-top: 24px; }
  .tl-call .tl-call-moment > .tl-sheet-gvacts, .tl-call-moment > .tl-call-acts { margin-top: 24px; }
  .tl-call-moment > .tl-call-near { margin-top: 36px; padding-top: 16px; gap: 6px; }
  .tl-call-moment > .tl-call-near .tl-call-nearh { margin: 4px 0 8px; }
}

/* part 2, the words: from the top, under the moment; the newest at the bottom once they scroll */
.tl-call-talk { position: relative; flex: 1 1 auto; min-height: 120px; overflow-y: auto; overscroll-behavior: contain; padding: 16px 20px; scrollbar-width: thin; }
.tl-call-talk:focus { outline: none; }
.tl-call-talk:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: -2px; }
.tl-call-talk.scrolled { -webkit-mask-image: linear-gradient(to bottom, transparent 0, var(--tl-ink) 28px); mask-image: linear-gradient(to bottom, transparent 0, var(--tl-ink) 28px); }
.tl-call-mark { display: flex; align-items: center; gap: 10px; margin-bottom: 12px !important; font-size: 12px; color: var(--tl-ink3); white-space: nowrap; }
.tl-call-mark::before, .tl-call-mark::after { content: ''; flex: 1; height: 1px; background: var(--tl-rule); }
.tl-cl + .tl-cl { margin-top: 14px; }
.tl-cl.guide + .tl-cl.you { margin-top: 18px; }   /* a question sits with its answer, not with what came before */
.tl-cl.you + .tl-cl.guide { margin-top: 10px; }
.tl-cl-lbl { margin-bottom: 3px !important; font-size: 12px; font-weight: 600; color: var(--tl-ink3); }
.tl-cl.guide .tl-cl-t { font-size: 15px; line-height: 1.5; color: var(--tl-ink2); }
.tl-cl.guide.latest .tl-cl-t { color: var(--tl-ink); }
.tl-cl.guide.cut .tl-cl-t::after { content: '\2026'; }
.tl-cl.you { display: flex; flex-direction: column; align-items: flex-end; }
.tl-cl.you .tl-cl-t { max-width: 86%; padding: 8px 12px 9px !important; border-radius: 14px 14px 4px 14px; background: var(--tl-tag); box-shadow: inset 0 0 0 1px var(--tl-rule); font-size: 14.5px; line-height: 1.45; color: var(--tl-ink); }
.tl-cl.m { margin-top: 20px !important; }
.tl-cl.m + .tl-cl { margin-top: 10px; }
.tl-cl-mbtn { display: flex; align-items: center; gap: 8px; width: 100%; min-height: 32px; font-size: 12.5px; font-weight: 500; color: var(--tl-ink2); }
.tl-cl-mbtn::before, .tl-cl-mbtn::after { content: ''; flex: 1; min-width: 12px; height: 1px; background: var(--tl-rule); }
.tl-cl-mbtn > span { display: inline-flex; align-items: center; gap: 6px; max-width: 84%; }
.tl-cl-mbtn em { font-style: normal; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tl-cl-mbtn:hover em { color: var(--tl-ink); text-decoration: underline; text-underline-offset: 2px; }
.tl-caret { display: inline-block; width: 2px; height: 1.05em; margin-left: 2px; vertical-align: -0.16em; background: var(--tl-ink); animation: tl-blink 1s steps(1) infinite; }
.tl-call-turn { display: flex; justify-content: flex-end; margin-top: 18px; }
.tl-call-turn > span { display: inline-flex; align-items: center; gap: 9px; min-height: 36px; padding: 0 14px 0 12px; border: 1.5px dashed var(--tl-rule2); border-radius: 14px 14px 4px 14px; font-size: 13.5px; color: var(--tl-ink2); }
.tl-level { display: inline-flex; align-items: center; gap: 2px; height: 16px; color: var(--tl-live); }
.tl-level i { display: block; width: 3px; height: 8px; border-radius: 2px; background: currentColor; }
.tl-call-ask { margin-top: 18px; }
.tl-call-ask p { font-size: 12px; font-weight: 500; color: var(--tl-ink3); }
.tl-call-ask ul { margin-top: 6px !important; display: grid; gap: 4px; }
.tl-call-ask li { font-size: 13.5px; line-height: 1.45; color: var(--tl-ink2); }
.tl-call-latest { position: sticky; bottom: 0; display: flex; justify-content: center; margin-top: 10px; pointer-events: none; }
.tl-call-latest button { pointer-events: auto; display: inline-flex; align-items: center; gap: 6px; height: 32px; padding: 0 12px 0 10px !important; border-radius: 999px; background: var(--tl-card) !important; border: 1px solid var(--tl-rule2) !important; box-shadow: 0 2px 10px var(--tl-shadow); font-size: 12.5px; font-weight: 600; color: var(--tl-ink); }

/* the row between the words and the dock: the connecting note, a warning, or the last call's words */
.tl-call-row { flex-shrink: 0; padding: 12px 20px; border-top: 1px solid var(--tl-rule); }
.tl-call-note { padding: 12px 14px !important; border: 1px dashed var(--tl-rule2); border-radius: 12px; font-size: 13px; line-height: 1.5; color: var(--tl-ink2); }
.tl-call-row.note { padding-bottom: 4px; }
.tl-call-warn { font-size: 13px; line-height: 1.45; color: var(--tl-ink); text-align: center; }
.tl-call-row.said { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding-top: 6px; padding-bottom: 6px; }
.tl-call-saidbtn { display: inline-flex; align-items: center; gap: 6px; min-height: 36px; font-size: 13.5px; font-weight: 600; color: var(--tl-ink); }
.tl-call-saidbtn[aria-expanded="true"] svg { transform: rotate(180deg); }
.tl-call-copy { display: inline-flex; align-items: center; gap: 6px; height: 36px; margin-right: -8px; padding: 0 8px !important; border-radius: 8px; font-size: 12.5px; font-weight: 600; color: var(--tl-ink2); }
.tl-call-copy:hover { background: var(--tl-tag); color: var(--tl-ink); }

/* part 3, the dock: one contact row that never moves; the call's two controls under it */
.tl-call-dock { flex-shrink: 0; padding: 14px 20px 16px; border-top: 1px solid var(--tl-rule); background: var(--tl-card); }
.tl-call-row + .tl-call-dock { border-top: 0; padding-top: 4px; }
.tl-call-row.said + .tl-call-dock { border-top: 1px solid var(--tl-rule); padding-top: 14px; }
.tl-call-row[hidden] + .tl-call-dock { border-top: 1px solid var(--tl-rule); padding-top: 14px; }
/* a short screen (a phone held sideways, a low laptop window): Mute and Hang up stay on screen, and the words, then the
   folded card, give up height first (review round 2, 29 Sep: the dock dropped below the screen mid-call) */
@media (max-height: 560px) {
  .tl-call:not(.phone) .tl-call-talk { min-height: 48px; }
  .tl-call:not(.phone) .tl-call-head { flex-shrink: 1; min-height: 0; overflow: hidden; }
}
.tl-call-who { display: flex; align-items: center; gap: 12px; min-height: 44px; }
/* the board's top-left "tR" (dark blue and yellow) on a white disc, like the nav shows it (Rossi, 29 Sep: "replace this
   tracketing logo with the one at the top left corner of the ai-board page, which has a white bg"); the thin ring
   keeps the disc's edge on the phone's light card (on the dark blue of the panel and the drawer it is all but
   invisible, and not needed) */
.tl-call-av { position: relative; width: 36px; height: 36px; flex-shrink: 0; border-radius: 50%; background: var(--tl-face-bg); box-shadow: 0 0 0 1px var(--tl-rule2); }
.tl-call-av img { display: block; box-sizing: border-box; width: 100%; height: 100%; padding: 7px 6px 7px 7px; border-radius: 50%; object-fit: contain; }
.tl-call-who.quiet .tl-call-av img { opacity: .5; }
.tl-call-who.speaking .tl-call-av::after { content: ''; position: absolute; inset: -4px; border-radius: 50%; border: 2px solid var(--tl-ink); animation: tl-pulse 1.4s ease-in-out infinite; }
.tl-call-who.connecting .tl-call-av::after { content: ''; position: absolute; inset: -4px; border-radius: 50%; border: 2px solid var(--tl-rule); border-top-color: var(--tl-ink); animation: tl-spin .9s linear infinite; }
.tl-call-id { flex: 1; min-width: 0; display: flex; flex-direction: column; line-height: 1.3; border-radius: 4px; }
.tl-call-id:focus { outline: none; }
.tl-call-id:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: 2px; }
.tl-call-id b { font-size: 14px; font-weight: 600; color: var(--tl-ink); }
/* (31 Oct) "Zin | Assistant IA": the bar lighter than the words on either side, in the title's own colour */
.tl-zin-sep { margin: 0 3px; font-weight: 400; font-style: normal; opacity: .45; }   /* an <i>, so code that finds the row's <span> line never takes it */
.tl-call-id span { font-size: 12.5px; color: var(--tl-ink3); font-variant-numeric: tabular-nums; }
.tl-dot { display: inline-block; width: 7px; height: 7px; margin-right: 6px; border-radius: 50%; background: var(--tl-live); vertical-align: 1px; }
.tl-dot.off { background: var(--tl-ink3); }
.tl-call-btn { flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; gap: 8px; height: 44px; padding: 0 18px 0 15px !important; border-radius: 999px; font-size: 14px; font-weight: 600; white-space: nowrap; border: 1px solid var(--tl-rule2) !important; background: var(--tl-card) !important; color: var(--tl-ink); }
.tl-call-btn:hover { border-color: var(--tl-ink3) !important; }
.tl-call-btn.primary { border-color: var(--tl-btn-bg) !important; background: var(--tl-btn-bg) !important; color: var(--tl-btn-fg); }
.tl-call-btn.pressed { background: var(--tl-tag) !important; }
.tl-call-btn.hang { border: 1.5px solid var(--tl-hang) !important; color: var(--tl-hang); }
.tl-call-btn.hang:hover, .tl-call-btn.hang:focus-visible { background: var(--tl-hang) !important; color: var(--tl-on-hang); }
.tl-call-btn[aria-disabled="true"] { opacity: .6; cursor: default; }
.tl-call-ctl { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.3fr); gap: 10px; margin-top: 12px; }
.tl-call-err { display: grid; grid-template-columns: 36px minmax(0, 1fr); column-gap: 12px; }
.tl-call-erri { width: 36px; height: 36px; display: flex; align-items: center; justify-content: center; border-radius: 50%; background: var(--tl-tag); color: var(--tl-alert); }
.tl-call-erri.calm { color: var(--tl-ink2); }
.tl-call-err b { display: block; font-size: 14px; font-weight: 700; color: var(--tl-ink); }
.tl-call-err p { margin-top: 3px !important; font-size: 13px; line-height: 1.5; color: var(--tl-ink2); }
.tl-call-err + .tl-call-ctl { display: flex; gap: 8px; margin-left: 48px; }

@keyframes tl-pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: .35; transform: scale(1.08); } }
@keyframes tl-spin { to { transform: rotate(360deg); } }
@keyframes tl-blink { 50% { opacity: 0; } }

/* ── the card's call button: 24px, last in the bottom row (after "Open post" on a large card), never growing it ── */
.tl-card .tl-bot { align-items: center; gap: 6px; }
/* one line, always: a large LinkedIn card is 194px inside, and "18,061 reactions", "Open post" and the phone
   need 192 (measured). A longer count shortens with an ellipsis rather than wrap onto a clipped second line. */
.tl-card .tl-bot > span:first-child { margin-right: auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tl-bot-r { display: inline-flex; flex-shrink: 0; align-items: center; gap: 6px; white-space: nowrap; }
/* 19px, a fifth smaller than the first 24 (Rossi, 29 Sep: "reduce the phone icon size by at least 20%"); the mouse
   still gets a 29px square to aim at (::after), and a finger 45px (below) */
.tl-callbtn { box-sizing: border-box; position: relative; flex-shrink: 0; width: 19px; height: 19px; margin: -2px 0; padding: 0; display: inline-flex; align-items: center; justify-content: center; border-radius: 50%; border: 1px solid var(--tl-rule2); background: var(--tl-card); color: var(--tl-call-green); cursor: pointer; pointer-events: auto; }
.tl-callbtn svg { width: 10px; height: 10px; display: block; }
.tl-callbtn::after { content: ''; position: absolute; inset: -6px; }   /* from inside the 1px border: 17 + 12 = 29px square */
.tl-callbtn:hover { background: var(--tl-tag); border-color: var(--tl-call-green); color: var(--tl-call-green); }
.tl-callbtn:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: 2px; }
.tl-callbtn.ring { border: 2px solid var(--tl-ink); color: var(--tl-ink); }
.tl-callbtn.live { border-color: var(--tl-live); background: var(--tl-live); color: var(--tl-on-live); }
/* a finger, not a mouse: the 19px phone's tap area grows to 45px, and the small "Open post" beside it gives way
   (the panel has the link), so a tap meant for the post can never start a paid call */
@media (pointer: coarse) {
  .tl-callbtn::after { content: ''; position: absolute; inset: -14px; }
  .tl-bot-r:has(.tl-callbtn) a { display: none; }
}

/* ONE tooltip for every control that needs its words said (DESIGN_SPEC 5.4). One element on <body>, drawn by script
   from the control's data-tip; it stays while the pointer is on the control or the words (::before bridges the gap). */
.tl-tip { position: fixed; z-index: 3000; width: max-content; max-width: min(280px, calc(100vw - 16px)); padding: 6px 10px; border-radius: 8px; background: var(--tl-ink); color: var(--tl-card); font-family: 'DM Sans', sans-serif; font-size: 12.5px; font-weight: 500; line-height: 1.3; box-shadow: 0 4px 12px var(--tl-shadow); }
.tl-tip::before { content: ''; position: absolute; left: 0; right: 0; top: -10px; height: 10px; }
.tl-tip.above::before { top: auto; bottom: -10px; }
.tl-tip[hidden] { display: none; }

/* during a call the story and the two controls that reload the timeline hold still (a reload would end the paid call):
   dimmed and described by the tooltip. The brand names are the view's title, so they stay ink; only the picker's
   marks step back. */
.tl-pcall { display: none; align-items: center; min-height: 40px; font-size: 13px; color: var(--tl-ink2); }
.tl-app.on-call .tl-pctl, .tl-app.on-call .tl-cap { display: none; }
.tl-app.on-call .tl-pcall { display: flex; }
.tl-app.on-call .tl-playbtn, .tl-app.on-call .tl-vplay, .tl-app.on-call .tl-platbtn { opacity: .45; cursor: not-allowed; }
/* (15 Oct, Rossi: the brand pickers stay free during a call, the call goes on with the new pair: no "not allowed" look) */

/* ── the phone: the call view inside the full-screen player (the moment folded into a compact card, then the words
   and the dock in one white sheet; the story's screens, caption, slot and controls give way) ── */
/* the story's slot for the call: the contact row, with an outlined Call (Pause stays the one filled button) */
.tl-fs-call { padding: 10px 12px 10px 14px; background: var(--tl-card); border: 1px solid var(--tl-rule); border-radius: 16px; }
.tl-fs-call[hidden] { display: none; }
.tl-fs-call .tl-call-btn { height: 48px; border: 1px solid var(--tl-rule2) !important; background: var(--tl-card); color: var(--tl-ink); }
.tl-fs-cv { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.tl-fs.calling .tl-fs-screens, .tl-fs.calling .tl-fs-dock { display: none; }
.tl-fs.on-call .tl-fs-x { visibility: hidden; }   /* Hang up is the way out of a call; the close comes back after it */
.tl-fs-cv .tl-call { flex: 1 1 auto; height: auto; background: transparent; }
.tl-call.phone .tl-call-head { margin: 2px 12px 0; padding: 12px 12px 12px 14px; background: var(--tl-card); border: 1px solid var(--tl-rule); border-top: 3px solid var(--tl-lane-0); border-radius: 16px; }
.tl-call.phone .tl-call-head.l1 { border-top-color: var(--tl-lane-1); }
.tl-call.phone .tl-call-ht { font-size: 17px; margin-top: 4px; }
.tl-call.phone .tl-call-head { margin-bottom: auto; }   /* before the first words the dock stays at the foot, no empty sheet */
.tl-fs-steps { display: flex; gap: 6px; margin-left: auto; }
.tl-fs-step { width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; border-radius: 22px; border: 1px solid var(--tl-rule) !important; color: var(--tl-ink); }
.tl-fs-step:disabled { opacity: .35; cursor: default; }
.tl-call.phone .tl-call-talk { margin-top: 10px; padding: 16px 18px; background: var(--tl-card); border-radius: 20px 20px 0 0; box-shadow: 0 -6px 24px var(--tl-shadow); }
.tl-call.phone .tl-call-row, .tl-call.phone .tl-call-dock { background: var(--tl-card); }
.tl-call.phone .tl-call-row { padding: 12px 16px; }
.tl-call.phone .tl-cl.guide .tl-cl-t { font-size: 16px; line-height: 1.45; }
.tl-call.phone .tl-cl.you .tl-cl-t { font-size: 15px; }
.tl-call.phone .tl-call-dock { padding: 12px 16px; padding-bottom: max(12px, env(safe-area-inset-bottom)); }
.tl-call.phone .tl-call-btn { height: 48px; }
.tl-call-back { width: 100%; min-height: 44px; margin-top: 10px; border: 1px solid var(--tl-rule2) !important; border-radius: 999px; background: var(--tl-card); font-size: 14.5px; font-weight: 600; }
/* a phone held sideways: the dock stays on screen, the words take what is left */
@media (max-height: 480px) {
  .tl-call.phone .tl-call-head { padding: 8px 10px; }
  .tl-call.phone .tl-call-hs { display: none; }
  .tl-call.phone .tl-call-talk { min-height: 60px; margin-top: 6px; padding: 10px 14px; }
  .tl-call.phone .tl-call-dock { padding-top: 8px; padding-bottom: 8px; }
  .tl-call.phone .tl-call-ctl { margin-top: 8px; }
}
/* a small phone held sideways, or 300% zoom (360px tall and less): Mute and Hang up stay on screen, and the compact card
   and the words give up their height (review round 2, 29 Sep: at 568x320 Hang up sat below the screen) */
@media (max-height: 360px) {
  .tl-call.phone .tl-call-head { flex-shrink: 1; min-height: 0; overflow: hidden; }
  .tl-call.phone .tl-call-talk { min-height: 0; }
}

/* a finger, not a mouse: every control reaches 44px */
@media (pointer: coarse) {
  .tl-call-more, .tl-call-copy, .tl-call-latest button { height: 44px; }
  .tl-call-open, .tl-call-playfrom, .tl-call-saidbtn, .tl-cl-mbtn { min-height: 44px; }
}
@media (prefers-reduced-motion: reduce) {
  .tl-call-who.speaking .tl-call-av::after, .tl-call-who.connecting .tl-call-av::after, .tl-caret { animation: none !important; }
  .tl-call-who.connecting .tl-call-av::after { border-style: dashed; }
  .tl-call-more svg, .tl-call-saidbtn svg { transition: none; }
  .tl-call-drawer { animation: none; }
}

/* ══ THE DESKTOP VERTICAL PROTOTYPE (Rossi, 30 Sep; the board's ?tlv=desk, local only) ══════════════════════════════
   The phone's list at desktop width: two brands' columns either side of the days, every post one card size, and the
   post's own picture or video carrying the page. Everything around the media stays quiet. */
.tl-app.desk .tl-vbody { padding: 0 24px; }
.tl-app.desk .tl-vlist { max-width: 1120px; margin: 0 auto; }
.tl-app.desk .tl-vhead { max-width: 1120px; margin: 0 auto; }
.tl-app.desk .tl-vrow { grid-template-columns: minmax(0, 1fr) 64px minmax(0, 1fr); }
.tl-app.desk .tl-vcol { gap: 12px; }
/* a post: its picture beside its words, one frame for both (the critique of the first pass, 30 Sep: a full-width
   picture let only two posts show per column on a laptop screen; beside the words, five do). A post with no picture
   is its words alone. A playing video opens the post out: the video at the column's full width, the words under it. */
.tl-vitem { position: relative; display: grid; grid-template-columns: minmax(0, 1fr); min-width: 0; overflow: hidden; background: var(--tl-card); border: 1px solid var(--tl-rule); border-radius: 12px; box-shadow: 0 1px 2px var(--tl-shadow); }
.tl-vitem.m { grid-template-columns: 42% minmax(0, 1fr); }
.tl-vitem.m.tall { grid-template-columns: 26% minmax(0, 1fr); }
.tl-vitem.tick { box-shadow: none; }
.tl-vitem.m.playing, .tl-vitem.m.tall.playing { grid-template-columns: minmax(0, 1fr); }   /* over the picture-beside-words rules */
/* one card size, but the moments that matter keep the phone's top edge in their brand's colour (prototype review, 30 Sep:
   with the sizing gone, a big launch looked exactly like a routine post) */
.tl-vitem.big { border-top-width: 3px; }
.tl-vitem.big.l0 { border-top-color: var(--tl-lane-0); }
.tl-vitem.big.l1 { border-top-color: var(--tl-lane-1); }
/* a desk has a pointer: a post answers it, as the wide timeline's cards do */
.tl-vitem:hover { border-color: var(--tl-rule2); }
.tl-vitem.big.l0:hover { border-top-color: var(--tl-lane-0); }
.tl-vitem.big.l1:hover { border-top-color: var(--tl-lane-1); }
.tl-vcard.desk { gap: 5px; min-height: 0; padding: 10px 12px; background: none; border: 0; border-radius: 0; box-shadow: none; justify-content: flex-start; }
.tl-vcard.desk .tl-vtop { gap: 8px; }
.tl-vnet { display: inline-flex; flex: none; width: 14px; height: 14px; color: var(--tl-ink2); }
.tl-vnet svg { width: 14px; height: 14px; }
.tl-vwhen { font-size: 12px; font-weight: 500; color: var(--tl-ink3); font-variant-numeric: tabular-nums; }
.tl-vcard.desk .tl-vttl { font-size: 14px; font-weight: 700; line-height: 1.28; letter-spacing: -0.005em; -webkit-line-clamp: 3; }
.tl-vitem.m:not(.playing) .tl-vcard.desk .tl-vttl { -webkit-line-clamp: 2; }
.tl-vcard.desk.tick .tl-vttl { font-weight: 600; }
/* the summary shows beside the picture too (prototype review, 30 Sep: hiding it left a blank band on every card) */
.tl-vsum { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; font-size: 13px; line-height: 1.4; color: var(--tl-ink2); }
/* (29 Oct) a school menu's week: one day a line, each cut at the card's edge, never clamped to two lines */
.tl-vsum.tl-vmenu { display: grid; gap: 1px; -webkit-line-clamp: unset; min-width: 0; }
.tl-vsum.tl-vmenu > span { display: block; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tl-vcard.desk .tl-vbot { margin-top: auto; padding-top: 2px; }
.tl-vviews { margin-left: 10px; color: var(--tl-ink3); }
/* the picture: it fills the post's height beside the words (16:9 at least; 4:5 for a tall video) */
.tl-vpic { all: unset; box-sizing: border-box; position: relative; display: block; width: 100%; min-height: 100%; aspect-ratio: 16 / 9; overflow: hidden; background: var(--tl-media); cursor: pointer; }
.tl-vpic.tall { aspect-ratio: 4 / 5; }
.tl-vpic img, .tl-vpic .tl-vcover { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
/* a line between the picture and the words (prototype review, 30 Sep: a white cover ran into the white card) */
.tl-vitem.m:not(.playing) .tl-vpic { border-right: 1px solid var(--tl-rule); }
/* playing: the column's full width, 16:9, or a tall video as high as the list can show with its words under it,
   centred (--vgap is measured when it opens: prototype review, 30 Sep: a two-thirds-of-the-window video was taller
   than the list on a 1280x800 laptop) */
.tl-vitem.playing .tl-vpic { min-height: 0; }
.tl-vitem.playing .tl-vpic.tall { aspect-ratio: 9 / 16; width: min(100%, calc((var(--vgap, 66vh) - 150px) * 9 / 16)); justify-self: center; }
.tl-vpic.on { background: var(--tl-ink); }
.tl-vpic video, .tl-vpic iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; object-fit: contain; }
.tl-vpic .tl-vcover { object-fit: cover; }
/* (31 Oct) a shelf of branded packs (dealMosaic): up to four in one cover, each WHOLE on white so its label reads (one
   cropped picture cut a Heineken pack to "eineken"); three = one large and two stacked, two = side by side. Same box
   on a phone's card (.tl-vmedia) and a desk's (.tl-vpic) */
.tl-mosaic { position: absolute; inset: 0; display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; gap: 1px; background: var(--tl-rule); }
.tl-mosaic.n2 { grid-template-rows: 1fr; }
.tl-mosaic.n3 img:first-child { grid-row: span 2; }
.tl-vpic .tl-mosaic img, .tl-vmedia .tl-mosaic img { position: static; width: 100%; height: 100%; min-width: 0; min-height: 0; box-sizing: border-box; padding: 4px; object-fit: contain; background: #fff; }
.tl-vmedia .tl-mosaic img { padding: 3px; }   /* a phone's tile is 71x40: every pixel goes to the pack */
/* the play control: the whole cover plays (prototype review, 30 Sep: only a 40px circle did, and it sat on the cover's
   own title), marked by the board's play button (dark blue, yellow) in the cover's corner */
.tl-vplayv { all: unset; box-sizing: border-box; position: absolute; inset: 0; display: block; cursor: pointer; }
/* half the size it was (Rossi, 1 Oct: "too big. Reduce their size by 50%"): 17px, the press area stays the whole cover */
.tl-vplayc { position: absolute; left: 6px; bottom: 6px; width: 17px; height: 17px; display: flex; align-items: center; justify-content: center; border-radius: 50%; background: var(--tl-btn-bg); color: var(--tl-btn-fg); box-shadow: 0 0 0 1px var(--tl-card); transition: transform .12s; }
.tl-vplayc svg { width: 7px; height: 7px; margin-left: 1px; }
.tl-vplayv:hover .tl-vplayc { transform: scale(1.08); }
.tl-vplayv:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: -4px; }
/* a picture: a press on it picks the post (as before); the magnifying glass in its corner, in the play button's dark
   blue and yellow, shows it large (Rossi, 1 Oct). The glass is its own button, 44px to press, never the whole cover */
.tl-vpicb { all: unset; box-sizing: border-box; position: absolute; inset: 0; display: block; cursor: pointer; }
.tl-vzoom { all: unset; box-sizing: border-box; position: absolute; left: 0; bottom: 0; z-index: 1; width: 44px; height: 44px; cursor: zoom-in; border-radius: 50%; }
.tl-vzoom .tl-vplayc { left: 6px; bottom: 6px; }
.tl-vzoom .tl-vplayc svg { width: 9px; height: 9px; margin: 0; stroke-width: 3.4; }   /* a thicker line, so the small glass still reads */
.tl-vzoom:hover .tl-vplayc { transform: scale(1.08); }
.tl-vzoom:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: 0; }
/* THE MAGNIFYING GLASS ON THE CARD THE ANALYST IS TALKING ABOUT (see vMarkZoom): the same mark as on the open card, in
   the same corner, so "there is more of this to see" is said the same way twice. A mark, not a button: the card under
   it is the button, and the next tap opens it whole */
.tl-vmag { position: absolute; top: 6px; right: 6px; z-index: 1; display: inline-flex; align-items: center; gap: 6px; height: 26px; padding: 0 4px; border-radius: 13px; pointer-events: none; animation: tl-vmag-in .18s ease-out; }
.tl-vmag.word { padding: 0 10px 0 4px; background: var(--tl-card); box-shadow: 0 1px 4px var(--tl-shadow), 0 0 0 1px var(--tl-rule); }
.tl-vmag.bare { position: static; align-self: flex-end; margin-top: 2px; }
.tl-vmag .tl-vplayc { position: static; width: 18px; height: 18px; box-shadow: 0 0 0 2px var(--tl-card); }
.tl-vmag .tl-vplayc svg { width: 9px; height: 9px; margin: 0; stroke-width: 3.4; }
/* the words, at the size of the card's own labels, gone once they have been read */
.tl-vmag-t { font-size: 11.5px; font-weight: 600; line-height: 1; color: var(--tl-ink); white-space: nowrap; }
.tl-vmag:not(.word) .tl-vmag-t { display: none; }
@keyframes tl-vmag-in { from { opacity: 0; transform: scale(.7); } }
@media (prefers-reduced-motion: reduce) { .tl-vmag { animation: none; } }
/* the picture, large: over the whole window, as big as it fits with its words under it */
.tl-pv-wrap { position: fixed; inset: 0; z-index: 2300; }
.tl-pv { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; padding: 56px 24px 24px; background: color-mix(in srgb, var(--tl-pv-bg) 94%, transparent); color: var(--tl-on-pv); cursor: zoom-out; }
.tl-pv-fig { margin: 0; display: flex; flex-direction: column; align-items: center; gap: 12px; max-width: min(100%, 1400px); max-height: 100%; cursor: default; }
.tl-pv-fig img { display: block; max-width: 100%; max-height: calc(100vh - 190px); max-height: calc(100dvh - 190px); object-fit: contain; border-radius: 8px; background: var(--tl-card); }
.tl-pv-fig figcaption { display: flex; flex-direction: column; align-items: center; gap: 4px; max-width: 760px; text-align: center; line-height: 1.35; }
.tl-pv-who { font-size: 13px; color: var(--tl-on-pv-2); }
.tl-pv-fig figcaption b { font-size: 16px; font-weight: 700; }
.tl-pv-fig figcaption a { margin-top: 2px; font-size: 13.5px; font-weight: 600; color: var(--tl-pv-link); text-decoration: underline; text-underline-offset: 2px; }
.tl-pv-x { all: unset; box-sizing: border-box; position: absolute; top: 12px; right: 12px; width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; border-radius: 50%; background: var(--tl-btn-bg); color: var(--tl-btn-fg); cursor: pointer; }
.tl-pv-x:focus-visible, .tl-pv-fig a:focus-visible { outline: 2px solid var(--tl-play); outline-offset: 3px; }
@media (max-width: 640px) { .tl-pv { padding: 60px 12px 16px; } .tl-pv-fig img { max-height: calc(100dvh - 210px); } }
/* a playing video's x, in the same dark blue and yellow: 16px to see (half its old size, Rossi 1 Oct), 44px to press */
.tl-vclose { all: unset; box-sizing: border-box; position: absolute; top: 6px; right: 6px; z-index: 2; width: 16px; height: 16px; display: flex; align-items: center; justify-content: center; border-radius: 50%; background: var(--tl-btn-bg); color: var(--tl-btn-fg); box-shadow: 0 0 0 1px var(--tl-card); cursor: pointer; }
.tl-vclose::after { content: ''; position: absolute; inset: -14px; }
.tl-vclose svg { width: 8px; height: 8px; stroke-width: 3.2; }
.tl-vclose:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: 3px; }
/* a video that will not play says so on its cover, with the post's own link */
.tl-vdead { position: absolute; left: 8px; right: 8px; bottom: 8px; padding: 6px 8px; border: 1px solid var(--tl-rule); border-radius: 8px; background: var(--tl-card); font-size: 12px; line-height: 1.35; color: var(--tl-ink); }
.tl-vdead a { color: inherit; font-weight: 600; text-decoration: underline; }
/* the moment being told or talked about: the whole post is ringed, never only its words; the same rules as the phone
   and the wide view (prototype review, 30 Sep: during a call two posts wore the thick ring). The story's moment: 3px;
   a post picked beside it: 2px; on a call, only the post being discussed, 3px. */
.tl-app.desk .tl-vcard.desk.current, .tl-app.desk .tl-vcard.desk.discussed, .tl-app.desk.on-call .tl-vcard.desk.current:not(.discussed) { box-shadow: none; }
.tl-vitem.l0:has(> .tl-vcard.current) { box-shadow: 0 0 0 3px var(--tl-lane-0); }
.tl-vitem.l1:has(> .tl-vcard.current) { box-shadow: 0 0 0 3px var(--tl-lane-1); }
.tl-vitem.l0:has(> .tl-vcard.discussed:not(.current)) { box-shadow: 0 0 0 2px var(--tl-lane-0); }
.tl-vitem.l1:has(> .tl-vcard.discussed:not(.current)) { box-shadow: 0 0 0 2px var(--tl-lane-1); }
.tl-app.on-call .tl-vitem:has(> .tl-vcard.current:not(.discussed)) { box-shadow: 0 1px 2px var(--tl-shadow); }
.tl-app.on-call .tl-vitem.l0:has(> .tl-vcard.discussed) { box-shadow: 0 0 0 3px var(--tl-lane-0); }
.tl-app.on-call .tl-vitem.l1:has(> .tl-vcard.discussed) { box-shadow: 0 0 0 3px var(--tl-lane-1); }
.tl-vitem:has(> .tl-vcard:focus-visible) { outline: 2px solid var(--tl-focus); outline-offset: 2px; }
.tl-vcard.desk:focus-visible { outline: none; }
/* the story steps every other post back, picture and all (prototype review, 30 Sep: only the words faded, so the other
   posts' pictures were the brightest things on screen); a call keeps them all at full strength, as on the phone */
.tl-app.desk .tl-vlist.playing .tl-vcard.desk:not(.current) { opacity: 1; }
.tl-app.desk .tl-vlist.playing .tl-vitem:not(:has(> .tl-vcard.current)) { opacity: .4; }
.tl-app.desk.on-call .tl-vlist.playing .tl-vitem { opacity: 1; }
.tl-app.desk .tl-vlist.playing .tl-vitem.ads.open { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .tl-vplayc { transition: none; } .tl-vplayv:hover .tl-vplayc { transform: none; } }
/* a follower milestone hugs its words on the spine's side of its column (review: stretched to 500px, the phone's capsule
   read as an empty field) */
.tl-app.desk .tl-vcol.l > .tl-vcard.ms { align-self: flex-end; width: auto; padding: 7px 16px; }
.tl-app.desk .tl-vcol.r > .tl-vcard.ms { align-self: flex-start; width: auto; padding: 7px 16px; }
/* the line from a day's dot reaches its post across the desk's wider middle (64px; the phone's is 48) */
.tl-app.desk .tl-vrow.has-l .tl-vdot::before, .tl-app.desk .tl-vrow.has-r .tl-vdot::after { width: 21px; }
.tl-app.desk .tl-vrow.has-l .tl-vdot.same::before, .tl-app.desk .tl-vrow.has-r .tl-vdot.same::after { width: 25px; }
/* "+N posts" on a desk: the folded posts' own pictures, then the count */
.tl-vchip em, .tl-vmore em { font-style: normal; }
.tl-vchip > .tl-vthumbs { display: inline-flex; align-items: center; gap: 4px; padding: 4px 11px 4px 4px; }
.tl-vthumbs img { width: 30px; height: 30px; flex: none; border-radius: 6px; object-fit: cover; background: var(--tl-media); }
.tl-vthumbs em { margin-left: 4px; }
/* the pinned bar on a desk: platform and filters on the left, the levels on the right. With no room for both on one
   line the filters wrap inside their half (prototype review, 30 Sep: they could neither wrap nor scroll, and ran under
   the call panel below about 1,080px); the levels always keep the right-hand end, the story running or not */
.tl-app.desk .tl-vsticky { margin: 0 -24px; padding: 0 24px; }
.tl-app.desk .tl-vtools { display: flex; align-items: center; gap: 16px; max-width: 1120px; margin: 0 auto; padding: 10px 0 8px; }
.tl-app.desk .tl-vtools .tl-controls { flex: 1 1 auto; min-width: 0; padding: 0; }
/* one row at every desk width (review, 30 Sep: at 1,280 and 1,366px "Milestones" wrapped alone onto a second row, and at
   1,052px the bar took three rows, a quarter of an 800px screen): tighter chips, the levels only as wide as their words,
   and where even that is too wide the filters scroll sideways under a fade, as on the phone */
.tl-app.desk .tl-vtools .tl-chips { flex-wrap: nowrap; gap: 4px; padding: 4px 3px; margin: -4px -3px; overflow-x: auto; scrollbar-width: none; -webkit-mask-image: linear-gradient(to right, var(--tl-ink) calc(100% - 28px), transparent); mask-image: linear-gradient(to right, var(--tl-ink) calc(100% - 28px), transparent); }
.tl-app.desk .tl-vtools .tl-chips::-webkit-scrollbar { display: none; }
.tl-app.desk .tl-vtools .tl-chips:not(.tl-fade) { -webkit-mask-image: none; mask-image: none; }
.tl-app.desk .tl-vtools .tl-chip, .tl-app.desk .tl-vtools .tl-seg button, .tl-app.desk .tl-vtools .tl-select { min-height: 34px; }
.tl-app.desk .tl-vtools .tl-chip { flex: none; padding: 0 9px; font-size: 13px; }
.tl-app.desk .tl-vtools .tl-zoom { flex: 0 0 auto; width: auto; margin-left: auto; }
.tl-app.desk .tl-vtools .tl-seg { flex: none; }
.tl-app.desk .tl-vtools .tl-seg button { flex: none; padding: 0 10px; font-size: 12.5px; }
/* a finger on a wide touch screen (an iPad Pro sideways gets the desk): the phone's 44px (review, 30 Sep) */
@media (pointer: coarse) {
  .tl-app.desk .tl-vtools .tl-chip, .tl-app.desk .tl-vtools .tl-seg button, .tl-app.desk .tl-vtools .tl-select, .tl-app.desk .tl-vhead .tl-vh-pick { min-height: 44px; }
  .tl-app.desk .tl-vhead .tl-vh-pick { margin: -9px 0; }
}
.tl-app.desk.playing .tl-vtools .tl-controls { display: flex; visibility: hidden; }
/* the follower header on one line (prototype review, 30 Sep: three lines per brand took a quarter of a 1280x800 screen):
   the first brand on the left, the week in the middle over the days, the second brand on the right */
.tl-app.desk .tl-vhead { grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: baseline; column-gap: 16px; min-height: 0; padding: 6px 0 9px; }
.tl-app.desk .tl-vh-wk { grid-column: 2; grid-row: 1; margin: 0; }
.tl-app.desk .tl-vh-b { grid-row: 1; display: flex; flex-wrap: wrap; align-items: baseline; column-gap: 8px; }
.tl-app.desk .tl-vh-b.l0 { grid-column: 1; }
.tl-app.desk .tl-vh-b.l1 { grid-column: 3; justify-content: flex-end; }
.tl-app.desk .tl-vh-nm { align-self: center; }
/* each brand's name is a picker on a desk (Rossi, 30 Sep: "we should have an option to select other brands"): a white
   pill with the brand's logo, its name and the menus' chevron, the same family as the platform menu beside the filters,
   so it reads as something to press; its list opens under it (.tl-pickmenu.head) with each brand's logo on the left */
/* (review, 30 Sep: the platform menu's own shape, 36px and an 8px corner, so it reads as a menu and not one more filter
   chip; it sits on the figures' baseline; it stays white when pointed at or open, the platform menu's way) */
.tl-app.desk .tl-vh-pick { display: inline-flex; align-self: baseline; align-items: center; gap: 7px; max-width: 100%; min-height: 36px; margin: -5px 0; padding: 0 9px 0 5px;
  border: 1px solid var(--tl-rule); border-radius: 8px; background: var(--tl-card); color: var(--tl-ink); font: inherit; font-size: 13.5px; font-weight: 700; cursor: pointer; }
.tl-app.desk .tl-vh-pick:hover { border-color: var(--tl-ink3); }
.tl-app.desk .tl-vh-pick.open { border-color: var(--tl-ctl); }
/* the second brand mirrors the first: its pill on the outer edge, its figures toward the middle, so the pill never moves
   as the week's numbers change width (review, 30 Sep: it slid 6 to 100px as the list scrolled, away from its open list) */
.tl-app.desk .tl-vh-b.l1 { flex-direction: row-reverse; justify-content: flex-start; }
.tl-app.desk .tl-vh-b.l1 .tl-vh-none { text-align: right; }
.tl-vh-na { font-size: 12px; font-weight: 500; color: var(--tl-ink2); }
/* while a pinned brand list is open the pinned bar rises over the story's bar, and the list shows half a row as the cue
   that it scrolls (placeHeadPick also keeps it above that bar) */
.tl-vsticky.picking { z-index: 7; }
.tl-pickmenu.head .tl-pick-list { max-height: 286px; }
.tl-app.desk .tl-vh-pick:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: 2px; }
.tl-app.desk .tl-vh-pick .tl-chev { flex-shrink: 0; color: var(--tl-ink3); transition: transform .15s ease; }
.tl-app.desk .tl-vh-pick.open .tl-chev { transform: rotate(180deg); }
.tl-pickmenu.head { margin-top: 0; }
.tl-app.desk .tl-vh-val { font-size: 17px; }
.tl-app.desk .tl-vh-chg { min-height: 0; font-size: 12px; }
/* why a brand's column is empty, under its figure */
.tl-vh-none { flex-basis: 100%; font-size: 12px; font-weight: 500; color: var(--tl-ink2); }

/* ══ THE BRANDS' ADS ON A DESK (Rossi, 30 Sep: "insert in this vertical timeline, ads of the respective account, something
   like x ads launched on [date] ... view & expand all ads with a slider"). One card per brand per day, in the brand's
   column after that day's posts, labelled like a post's type ("Ads"): the count, the networks, and a strip of the first
   pictures. Pressed, it opens in place into a slider, one ad at a time with the next one showing at the edge. ══ */
.tl-vitem.ads { display: block; }
.tl-vads { all: unset; box-sizing: border-box; display: flex; flex-direction: column; gap: 7px; width: 100%; padding: 10px 12px 12px; cursor: pointer; }
.tl-vitem:has(> .tl-vads:focus-visible), .tl-vitem.ads:has(.tl-vads-h:focus-visible) { outline: 2px solid var(--tl-focus); outline-offset: 2px; }
.tl-vads .tl-vtop, .tl-vads-h { display: flex; align-items: center; gap: 8px; min-width: 0; }
.tl-adnets { display: inline-flex; align-items: center; gap: 6px; margin-left: auto; }
.tl-adnet { display: inline-flex; flex: none; width: 14px; height: 14px; }
.tl-adnet svg { width: 14px; height: 14px; }
.tl-adhead { min-width: 0; font-size: 13.5px; line-height: 1.35; color: var(--tl-ink2); }
.tl-adhead b { font-size: 14px; font-weight: 700; color: var(--tl-ink); }
.tl-adstrip { display: flex; gap: 6px; }
.tl-adtile { position: relative; flex: none; width: 58px; height: 58px; overflow: hidden; display: inline-flex; align-items: center; justify-content: center; border: 1px solid var(--tl-rule); border-radius: 8px; background: var(--tl-media); color: var(--tl-ink3); font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; }
.tl-adtile img { width: 100%; height: 100%; display: block; object-fit: cover; }
/* a video ad's tile carries the board's play mark, small, in its corner */
.tl-adtile.vid::after { content: ''; position: absolute; left: 5px; bottom: 5px; width: 16px; height: 16px; border-radius: 50%; background: var(--tl-btn-bg); box-shadow: 0 0 0 1.5px var(--tl-card); }
.tl-adtile.vid::before { content: ''; position: absolute; z-index: 1; left: 11px; bottom: 9.5px; border-style: solid; border-width: 3.5px 0 3.5px 6px; border-color: transparent transparent transparent var(--tl-btn-fg); }
.tl-adtile.more { background: var(--tl-tag); color: var(--tl-ink2); }
/* one different ad: its picture beside its headline, read at once without opening */
.tl-adstrip.one { align-items: center; gap: 10px; }
.tl-adone { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; min-width: 0; font-size: 13.5px; font-weight: 600; line-height: 1.35; color: var(--tl-ink); }
/* text ads only: the first headline in quotes, instead of empty tiles */
.tl-adquote { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13.5px; font-weight: 600; color: var(--tl-ink); }
.tl-admore { flex: none; font-size: 12.5px; color: var(--tl-ink2); }
.tl-adstrip:has(.tl-adquote) { align-items: baseline; gap: 6px; }
/* at 3 months and 1 year a small group is a chip, the size of "+3 posts" */
.tl-vitem.ads.chip { align-self: flex-start; background: none; border: 0; box-shadow: none; overflow: visible; }
.tl-vcol.l > .tl-vitem.ads.chip { align-self: flex-end; }
.tl-tag.tl-adtag { flex-shrink: 0; color: var(--tl-on-ad); font-weight: 700; letter-spacing: .01em; }
.tl-adtag.meta { background: var(--tl-ad-meta); }
.tl-adtag.google { background: var(--tl-ad-google); }
.tl-adtag.linkedin { background: var(--tl-ad-linkedin); }
/* THE LABEL IS THE NETWORK'S LOGO (Rossi, 8 Oct). The logo brings its own colours, so the chip turns light and keeps
   the network's colour as a ring and on its word; a filled chip would bury a blue logo in a blue field. The rules
   above stay for a board that hands the timeline no logos, where the chip is still the coloured name. */
.tl-adtag.logo { display: inline-flex; align-items: center; gap: 6px; padding: 3px 9px; background: var(--tl-card); }
.tl-adtag.logo.meta { box-shadow: inset 0 0 0 1.5px var(--tl-ad-meta); }
.tl-adtag.logo.google { box-shadow: inset 0 0 0 1.5px var(--tl-ad-google); }
.tl-adtag.logo.linkedin { box-shadow: inset 0 0 0 1.5px var(--tl-ad-linkedin); }
.tl-adlogo { display: inline-flex; align-items: center; gap: 4px; }
.tl-adlogo svg { display: block; width: 14px; height: 14px; }
.tl-adtag-w { font-size: 11.5px; font-weight: 700; font-variant-numeric: tabular-nums; }
.tl-adtag.logo.meta .tl-adtag-w { color: var(--tl-ad-meta); }
.tl-adtag.logo.google .tl-adtag-w { color: var(--tl-ad-google); }
.tl-adtag.logo.linkedin .tl-adtag-w { color: var(--tl-ad-linkedin); }
.tl-adchip { flex-direction: row; align-items: center; gap: 8px; width: auto; min-height: 34px; padding: 4px 13px 4px 10px; border: 1px solid var(--tl-rule); border-radius: 999px; background: var(--tl-card); }
.tl-adchip:hover { border-color: var(--tl-rule2); }
.tl-adchip .tl-adnets { margin-left: 0; }
.tl-adchip em { font-style: normal; font-size: 13px; font-weight: 600; color: var(--tl-ink); white-space: nowrap; }
.tl-vitem.ads.chip:has(> .tl-vads:focus-visible) { outline: none; }
.tl-adchip:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: 2px; }
/* "See all": every different ad at a glance, a press opens it in the slider */
.tl-adall { all: unset; box-sizing: border-box; flex: none; min-height: 32px; padding: 0 11px; border-radius: 8px; box-shadow: inset 0 0 0 1px var(--tl-rule2); font-size: 12.5px; font-weight: 600; color: var(--tl-ctl); cursor: pointer; white-space: nowrap; }
.tl-adall:hover { background: var(--tl-tag); }
.tl-adall:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: 2px; }
.tl-adgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 8px; max-height: 420px; overflow-y: auto; padding: 12px; overscroll-behavior: contain; }
.tl-adgo { all: unset; box-sizing: border-box; position: relative; aspect-ratio: 1 / 1; overflow: hidden; border: 1px solid var(--tl-rule); border-radius: 8px; background: var(--tl-media); cursor: pointer; }
.tl-adgo img { width: 100%; height: 100%; display: block; object-fit: cover; }
.tl-adgo:hover { border-color: var(--tl-ink3); }
.tl-adgo:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: 2px; }
.tl-adgotxt { display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; padding: 8px; font-size: 11.5px; font-weight: 600; line-height: 1.3; color: var(--tl-ink); background: var(--tl-canvas); height: 100%; box-sizing: border-box; }
.tl-adgon { position: absolute; right: 5px; bottom: 5px; padding: 1px 6px; border-radius: 999px; background: var(--tl-card); font-size: 11px; font-weight: 700; color: var(--tl-ink); box-shadow: 0 0 0 1px var(--tl-rule); }
.tl-adgoplay { position: absolute; left: 5px; bottom: 5px; width: 18px; height: 18px; border-radius: 50%; background: var(--tl-btn-bg); box-shadow: 0 0 0 1.5px var(--tl-card); }
.tl-adgoplay::after { content: ''; position: absolute; left: 7px; top: 5px; border-style: solid; border-width: 4px 0 4px 6px; border-color: transparent transparent transparent var(--tl-btn-fg); }
/* where the ads stop: when we last checked them, on the spine like "Posts tracked from" */
.tl-vmark.adsseen span { color: var(--tl-ink2); font-weight: 500; }
/* opened: a bar with the count, where you are ("2 of 5"), the arrows (the board's dark blue and yellow) and the x */
/* the controls step down to a second row when the labels and the words need the room, never cutting them (review,
   1 Oct: two network labels left "11 ads, 9 Jul" ten pixels at 1280px) */
.tl-adbar { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 8px 10px 8px 12px; border-bottom: 1px solid var(--tl-rule); }
.tl-vads-h { all: unset; box-sizing: border-box; flex: 1 0 auto; display: flex; align-items: center; gap: 8px; min-width: 0; max-width: 100%; min-height: 32px; cursor: pointer; }
.tl-adctl { flex: none; display: flex; align-items: center; gap: 6px; margin-left: auto; }
.tl-adctl .tl-adcount { margin-right: 0; }
.tl-adctl .tl-adnav.x { margin-left: 0; }
.tl-vads-h .tl-adhead { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tl-adcount { flex: none; margin-right: 4px; font-size: 12.5px; font-weight: 600; color: var(--tl-ink2); font-variant-numeric: tabular-nums; white-space: nowrap; }
.tl-adnav { all: unset; box-sizing: border-box; position: relative; flex: none; width: 32px; height: 32px; display: inline-flex; align-items: center; justify-content: center; border-radius: 50%; background: var(--tl-btn-bg); color: var(--tl-btn-fg); cursor: pointer; }
.tl-adnav::after { content: ''; position: absolute; inset: -6px; }
.tl-adnav:disabled { opacity: .3; cursor: default; }
.tl-adnav.x { margin-left: 4px; background: var(--tl-card); color: var(--tl-ink2); box-shadow: inset 0 0 0 1px var(--tl-rule2); }
.tl-adnav svg { width: 15px; height: 15px; }
.tl-adnav:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: 2px; }
.tl-adtrack { display: flex; gap: 10px; padding: 12px; overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x mandatory; scroll-padding: 0 12px; scrollbar-width: thin; }
.tl-adtrack:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: -3px; }
/* each ad: 86% of the column, so the next one shows at the edge and the slider reads as one */
.tl-adslide { flex: 0 0 86%; min-width: 0; display: flex; flex-direction: column; overflow: hidden; scroll-snap-align: start; background: var(--tl-card); border: 1px solid var(--tl-rule); border-radius: 10px; }
.tl-adtrack.one .tl-adslide { flex-basis: 100%; }
.tl-admedia { position: relative; aspect-ratio: 4 / 3; max-height: 60vh; background: var(--tl-media); }
.tl-admedia.on { background: var(--tl-ink); }
.tl-admedia iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
/* an ad's video: the board's play button in the cover's corner; the whole cover plays it (as a post's video does) */
.tl-adplay { all: unset; box-sizing: border-box; position: absolute; inset: 0; display: block; cursor: pointer; }
.tl-adplay:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: -4px; }
.tl-adplay:hover .tl-vplayc { transform: scale(1.08); }
.tl-admedia img, .tl-admedia video { position: absolute; inset: 0; width: 100%; height: 100%; display: block; object-fit: contain; }
/* a text ad (Google's search ads), shown the way it reads: where it leads, then its words */
.tl-admedia.text { aspect-ratio: auto; max-height: none; display: flex; flex-direction: column; justify-content: center; gap: 6px; padding: 14px 18px; background: var(--tl-canvas); }
.tl-adsite { font-size: 12px; color: var(--tl-ink2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tl-adtxt { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; font-size: 16px; font-weight: 700; line-height: 1.3; color: var(--tl-ink); }
.tl-adtxtb { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; font-size: 13px; line-height: 1.4; color: var(--tl-ink2); }
/* an ad in several versions says so beside where it ran */
.tl-advers { margin-left: auto; padding: 1px 7px; border-radius: 999px; background: var(--tl-tag); font-size: 11.5px; font-weight: 600; color: var(--tl-ink2); white-space: nowrap; }
.tl-adwords { display: flex; flex-direction: column; gap: 4px; padding: 10px 12px 12px; }
.tl-adwhere { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--tl-ink2); }
.tl-adran { font-size: 12px; color: var(--tl-ink3); font-variant-numeric: tabular-nums; }
.tl-adt { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; margin-top: 2px; font-size: 14px; font-weight: 700; line-height: 1.3; color: var(--tl-ink); }
.tl-adb { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; font-size: 13px; line-height: 1.4; color: var(--tl-ink2); }
.tl-adlink { align-self: flex-start; max-width: 100%; margin-top: 4px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; font-weight: 600; color: var(--tl-ink); text-decoration: underline; text-underline-offset: 2px; }
.tl-adlink:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: 2px; border-radius: 2px; }
@media (prefers-reduced-motion: reduce) { .tl-adtrack { scroll-behavior: auto; } }

/* THE PHONE'S TOP, MOBILE ROUND (Rossi, 1 Oct, his screenshot): the title row ("OpenAI vs Anthropic" and "All platforms,
   6 Jul to 1 Oct 2026") is put away to gain height, still read out as the timeline's heading; the platform menu and the
   filters are the one pinned row (.tl-vsticky); the level buttons and the follower counts come next and scroll with the
   list, and each brand's name in the counts is its picker, as on a desk */
.tl-app.phone .tl-head { position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.tl-app.phone .tl-vsticky .tl-controls { padding: 8px 0 8px 14px; }
.tl-app.phone .tl-vbody > .tl-vtools { margin: 0 -14px; padding: 10px 14px 0; }
/* a sample frame on a marketing page asks for no zoom row at all (cfg.levels false, 14 Oct): the four span buttons,
   the phone's level menu and the + / - keys all go, and the frame spends that height on the posts instead */
.tl-app.no-levels .tl-vtools, .tl-app.no-levels .tl-zoom, .tl-app.no-levels .tl-lvlsel { display: none !important; }
.tl-app.phone .tl-vbody > .tl-vhead { margin: 0 -14px; border-bottom: 1px solid var(--tl-rule); }
.tl-app.phone .tl-vh-pick { position: relative; display: inline-flex; align-items: center; gap: 6px; max-width: 100%; min-height: 34px; margin: 2px 0 4px; padding: 0 8px 0 4px;
  border: 1px solid var(--tl-rule); border-radius: 8px; background: var(--tl-card); color: var(--tl-ink); font: inherit; font-size: 13.5px; font-weight: 700; cursor: pointer; }
.tl-app.phone .tl-vh-pick::after { content: ''; position: absolute; inset: -5px -2px; }   /* 44px to press */
.tl-app.phone .tl-vh-pick .tl-chev { flex-shrink: 0; color: var(--tl-ink3); transition: transform .15s ease; }
.tl-app.phone .tl-vh-pick.open { border-color: var(--tl-ctl); }
.tl-app.phone .tl-vh-pick.open .tl-chev { transform: rotate(180deg); }
.tl-app.phone .tl-vh-pick:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: 2px; }
/* sized from the box it hangs in, not the screen: a phone-width list beside the board's panels is far narrower than the window (review, 1 Oct) */
.tl-app.phone .tl-pickmenu.head { width: calc(100% - 28px); max-width: 360px; }
@media (prefers-reduced-motion: reduce) { .tl-app.phone .tl-vh-pick .tl-chev { transition: none; } }

/* ══ THE BRANDS' ADS ON A PHONE (Rossi, 1 Oct: "I believe the ads do not currently show up in the mobile version. Can you
   add them and use all your skills to ensure the UX is at its best!"). A day's ads are a small pill in the brand's column,
   hugging the line: each network's label in its colour (11.5px, the board's label size, never smaller) and "23 ads";
   28px to see, 44px to press. Pressed, a sheet from the bottom holds them: the network tabs, one ad at a time with the
   next at the edge, and the arrows, "3 of 9" and "See all" pinned at its foot. ══ */
.tl-vads.tl-adstub { all: unset; box-sizing: border-box; align-self: flex-start; display: flex; align-items: center; min-height: 44px; max-width: 100%; cursor: pointer; -webkit-tap-highlight-color: transparent; }
.tl-vcol.l > .tl-adstub { align-self: flex-end; }
.tl-vcol > .tl-adstub:only-child { margin-top: 12px; }
/* (design critique, 1 Oct) the count and its date first, "3 ads · 8 Sep", a chevron that says it opens, the networks'
   labels under them; a column that shrinks to its words, so the pill stays by the line */
.tl-adstub-p { display: inline-flex; flex-direction: column; align-items: flex-start; gap: 4px; max-width: 100%; padding: 6px 10px; border: 1px solid var(--tl-rule); border-radius: 10px; background: var(--tl-card); box-shadow: 0 1px 2px var(--tl-shadow); }
/* the ads themselves on the card (Rossi, 2 Oct): one picture, or two side by side, under the networks' labels */
.tl-vads.tl-adstub.has-pic, .tl-vads.tl-adstub.has-say { align-self: stretch; }
.tl-adstub.has-pic .tl-adstub-p, .tl-adstub.has-say .tl-adstub-p { width: 100%; padding: 6px; gap: 6px; }
.tl-adstub.has-pic .tl-adstub-n, .tl-adstub.has-say .tl-adstub-n { padding: 0 4px; }
.tl-adstub.has-pic .tl-adstub-h, .tl-adstub.has-say .tl-adstub-h { padding: 0 4px 2px; }
.tl-adstub-pics { display: grid; grid-template-columns: 1fr; gap: 4px; width: 100%; }
/* one creative across the pill, in the shape its network's creatives come in (adShape, measured on all 601), so
   nothing is cropped and the list never jumps as a picture loads. A square pill stands about as tall as the post card
   beside it (Rossi, 2 Oct: "the same height as the card next"); a search ad is a short strip, which is what it is */
.tl-adstub-pic { position: relative; display: block; overflow: hidden; border-radius: 7px; background: var(--tl-pv-bg); aspect-ratio: 1 / 1; }
/* a search ad is a WHITE screenshot: blurred and darkened behind itself it came out as a grey letterbox (design
   critique, 8 Oct), so it sits on white inside a hairline, the way the sheet already shows Google's */
.tl-adstub-pic.wide { aspect-ratio: 2 / 1; background: var(--tl-card); }
.tl-adstub-pic.wide::after { content: ''; position: absolute; inset: 0; border-radius: inherit; box-shadow: inset 0 0 0 1px var(--tl-rule); pointer-events: none; }
.tl-adstub-pic.video { aspect-ratio: 16 / 9; }
/* THE WHOLE CREATIVE, NOT THE MIDDLE OF IT (measured 8 Oct: 50 of 90 tiles threw away half or more; a 1080 by 1920
   video still in a 16:9 tile lost 69%). The same answer as a post's picture in the card panel: whole, on a blurred
   copy of itself, so the tile is full at every shape and the ad is recognisable */
.tl-adstub-pic img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; display: block; }
.tl-adstub-pic img.tl-adstub-blur { inset: -10%; width: 120%; height: 120%; object-fit: cover; filter: blur(12px) saturate(1.3) brightness(.78); }
/* an ad with words and no picture: drawn as a small search result (where it ran, then its headline), so it reads as
   the ad it is. A grey block of bold words looked like a disabled button (design critique, 8 Oct) */
.tl-adstub-say { display: flex; flex-direction: column; gap: 2px; width: 100%; padding: 7px 9px 8px; border-radius: 7px; background: var(--tl-card); box-shadow: inset 0 0 0 1px var(--tl-rule); }
.tl-adstub-say-h { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11px; line-height: 1.3; color: var(--tl-ink2); }
.tl-adstub-say-t { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; font-size: 13px; font-weight: 500; line-height: 1.3; color: var(--tl-ink); }
.tl-adstub-say.g .tl-adstub-say-t { color: var(--tl-ad-headline); }
.tl-adstub-play { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); width: 26px; height: 26px; display: flex; align-items: center; justify-content: center; border-radius: 50%; background: var(--tl-btn-bg); color: var(--tl-btn-fg); }
.tl-adstub-play svg { width: 13px; height: 13px; }
.tl-adstub-p .tl-adtag { padding: 2px 7px; font-size: 11.5px; line-height: 16px; }
.tl-adstub-p .tl-adtag.logo { padding: 3px 8px; }
/* "3 ads": the word in the Meta blue beside the black figure, so the thing being counted is the thing you see first
   (Rossi, 8 Oct). It is one <b>, so the figure and its word never break across two lines */
.tl-adstub-w { color: var(--tl-ad-meta); }
/* the date steps under the count when the column is too narrow; its dot sits in the 12px each item keeps on its left.
   The row inside is pulled 12px left and the box around it clips at the words' left edge, so a date that starts a line
   has its dot cut away and never begins with one */
.tl-adstub-h { display: block; max-width: 100%; overflow: hidden; font-size: 12.5px; line-height: 18px; color: var(--tl-ink); font-variant-numeric: tabular-nums; }
.tl-adstub-hi { display: flex; flex-wrap: wrap; align-items: center; margin-left: -12px; }
.tl-adstub-hi > b, .tl-adstub-d { margin-left: 12px; white-space: nowrap; }
.tl-adstub-hi > b { font-weight: 700; }
.tl-adstub-d { position: relative; display: inline-flex; align-items: center; font-weight: 600; }
.tl-adstub-sep { position: absolute; left: -8px; color: var(--tl-ink3); }
.tl-adstub-d .tl-chev { flex: none; width: 12px; height: 12px; margin-left: 4px; transform: rotate(-90deg); color: var(--tl-ink3); }
.tl-adstub-n { display: flex; flex-wrap: wrap; gap: 4px; }
/* where the ads stop, per network, and under Today the way up to the newest ads: a pill on the canvas, which also hides
   the centre line below Today */
.tl-vmark.adsnew { margin: -4px 0 0; padding: 14px 0 6px; background: var(--tl-canvas); }
.tl-adsnew { all: unset; box-sizing: border-box; position: relative; z-index: 1; min-height: 44px; display: inline-flex; align-items: center; gap: 6px; padding: 0 16px; border: 1px solid var(--tl-rule2); border-radius: 999px; background: var(--tl-card); font-size: 13px; font-weight: 600; color: var(--tl-ink); cursor: pointer; }
.tl-adsnew:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: 2px; }
.tl-adstub:hover .tl-adstub-p { border-color: var(--tl-rule2); }
.tl-adstub:focus-visible { outline: none; }
.tl-adstub:focus-visible .tl-adstub-p { outline: 2px solid var(--tl-focus); outline-offset: 2px; }
.tl-vlist.playing .tl-adstub { opacity: .4; }
.tl-app.on-call .tl-vlist.playing .tl-adstub { opacity: 1; }
/* the sheet: it slides up (at once with reduced motion); its foot carries the controls, so it has no padding of its own */
/* The ads sheet keeps ONE height (Rossi, 2 Oct: swiping between ads of different sizes made it "jump up and down"),
   so the sheet is a column of its own height: the heading, then the ads, which take what is left and scroll inside. */
.tl-sheet.tl-adsheet { padding-bottom: 0; animation: tl-adsheet-in .2s ease-out; height: 95vh; height: 95svh; max-height: 95svh; display: flex; flex-direction: column; overflow: hidden; }
.tl-sheet.tl-adsheet.nofoot { padding-bottom: calc(18px + env(safe-area-inset-bottom)); }
@keyframes tl-adsheet-in { from { transform: translateY(100%); } }
.tl-adsheet-tabs { display: flex; gap: 8px; margin: 4px -18px 0; padding: 0 18px; overflow-x: auto; scrollbar-width: none; }
.tl-adsheet-tabs::-webkit-scrollbar { display: none; }
.tl-adjump { all: unset; box-sizing: border-box; flex: none; min-height: 44px; display: inline-flex; align-items: center; padding: 0 4px; border-radius: 999px; cursor: pointer; }
/* (design critique, 1 Oct) a network not on screen: white, its name and a 1.5px edge in its colour; the one on screen:
   filled, white words. No black ring */
.tl-adjump .tl-adtag { padding: 4px 10px; font-size: 12.5px; line-height: 16px; background: var(--tl-card); }
.tl-adjump .tl-adtag.meta { color: var(--tl-ad-meta); box-shadow: inset 0 0 0 1.5px var(--tl-ad-meta); }
.tl-adjump .tl-adtag.google { color: var(--tl-ad-google); box-shadow: inset 0 0 0 1.5px var(--tl-ad-google); }
.tl-adjump .tl-adtag.linkedin { color: var(--tl-ad-linkedin); box-shadow: inset 0 0 0 1.5px var(--tl-ad-linkedin); }
.tl-adjump[aria-current="true"] .tl-adtag { color: var(--tl-on-ad); box-shadow: none; }
.tl-adjump[aria-current="true"] .tl-adtag.meta { background: var(--tl-ad-meta); }
.tl-adjump[aria-current="true"] .tl-adtag.google { background: var(--tl-ad-google); }
.tl-adjump[aria-current="true"] .tl-adtag.linkedin { background: var(--tl-ad-linkedin); }
/* the logo tabs: the network on screen is the one with a filled tint and a solid ring, since a filled chip would hide
   the logo it is made of. The count beside it stays in the network's colour, dark enough to read on the tint */
.tl-adjump .tl-adtag.logo { padding: 4px 11px; }
.tl-adjump[aria-current="true"] .tl-adtag.logo.meta { background: color-mix(in srgb, var(--tl-ad-meta) 13%, var(--tl-card)); box-shadow: inset 0 0 0 2px var(--tl-ad-meta); }
.tl-adjump[aria-current="true"] .tl-adtag.logo.google { background: color-mix(in srgb, var(--tl-ad-google) 13%, var(--tl-card)); box-shadow: inset 0 0 0 2px var(--tl-ad-google); }
.tl-adjump[aria-current="true"] .tl-adtag.logo.linkedin { background: color-mix(in srgb, var(--tl-ad-linkedin) 13%, var(--tl-card)); box-shadow: inset 0 0 0 2px var(--tl-ad-linkedin); }
.tl-adjump[aria-current="true"] .tl-adtag.logo.meta .tl-adtag-w { color: var(--tl-ad-meta); }
.tl-adjump[aria-current="true"] .tl-adtag.logo.google .tl-adtag-w { color: var(--tl-ad-google); }
.tl-adjump[aria-current="true"] .tl-adtag.logo.linkedin .tl-adtag-w { color: var(--tl-ad-linkedin); }
/* the count with every repeat of the same ad, under the title */
.tl-adsheet-sub { margin: 2px 0 0; font-size: 13px; line-height: 1.35; color: var(--tl-ink2); }
.tl-adjump:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: -2px; }
/* the desk's open group, without its card: the slider runs to the screen's edges so the next ad peeks there */
.tl-adsheet .tl-vitem.ads.open { overflow: visible; margin-top: 2px; border: 0; border-radius: 0; box-shadow: none; background: none; flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
/* (each ad its own height: stretched to the tallest, a short one was a card with an empty half) */
.tl-adsheet .tl-adtrack { align-items: stretch; margin: 0 -18px; padding: 8px 18px 14px; scroll-padding: 0 18px; scrollbar-width: none; overflow-y: hidden; flex: 1 1 auto; min-height: 0; height: auto !important; transition: none; }
.tl-adsheet .tl-adtrack::-webkit-scrollbar { display: none; }
.tl-adsheet .tl-adtrack:focus-visible { outline-offset: -2px; }
.tl-adsheet .tl-adslide { flex-basis: 90%; overflow-y: auto; overscroll-behavior: contain; }   /* a long ad scrolls inside its own slide, so the sheet never resizes under the reader */
.tl-adsheet .tl-adtrack.one .tl-adslide { flex-basis: 100%; }
/* (design critique, 1 Oct) a picture's box takes the picture's own shape once it loads (4:3 until then, 0.8 to 2.2, at
   most 40% of the screen's height, its width cut to match), so the glass and the play circle sit on its corner; Google's
   pictures, white screenshots, on white; a still picture opens large where it is pressed */
.tl-adsheet .tl-admedia { width: 100%; max-width: calc(40vh * var(--ar, 1.3334)); max-width: calc(40dvh * var(--ar, 1.3334)); max-height: 40vh; max-height: 40dvh; margin: 0 auto; }
.tl-adsheet .tl-admedia.g { background: var(--tl-card); }
.tl-adsheet .tl-admedia .tl-vzoom { left: var(--inx, 0%); bottom: var(--iny, 0%); }
.tl-adsheet .tl-adplay .tl-vplayc { left: calc(var(--inx, 0%) + 6px); bottom: calc(var(--iny, 0%) + 6px); }
.tl-adsheet .tl-admedia[data-a="adzoom"] { cursor: zoom-in; }
.tl-adsheet .tl-admedia.text { max-width: none; max-height: none; }
.tl-admedia.nopic { display: none; }
.tl-adsheet .tl-adwords { gap: 5px; padding: 12px 14px 14px; }
.tl-adsheet .tl-adwhere, .tl-adsheet .tl-adran { font-size: 13px; }
.tl-adsheet .tl-adt { font-size: 15.5px; }
.tl-adsheet .tl-adb { font-size: 14px; }
/* the ad's words in full (the desk's card clamps them to three lines) */
.tl-adsheet .tl-adt, .tl-adsheet .tl-adb, .tl-adsheet .tl-adtxt, .tl-adsheet .tl-adtxtb { display: block; -webkit-line-clamp: unset; overflow: visible; }
.tl-adsheet .tl-adlink { display: inline-block; min-height: 44px; margin-top: 6px; padding: 0 16px; line-height: 42px; border: 1px solid var(--tl-rule2); border-radius: 999px; font-size: 14px; text-decoration: none; }
/* the controls, pinned to the sheet's foot: Previous, "3 of 9", Next, then "See all" at the right */
.tl-adsheet .tl-adctl { position: sticky; bottom: 0; z-index: 2; margin: 0 -18px; padding: 8px 18px calc(10px + env(safe-area-inset-bottom)); display: flex; align-items: center; gap: 12px; background: var(--tl-card); border-top: 1px solid var(--tl-rule); }
.tl-adsheet .tl-adnav { width: 44px; height: 44px; }
.tl-adsheet .tl-adnav::after { content: none; }
.tl-adsheet .tl-adnav svg { width: 18px; height: 18px; }
.tl-adsheet .tl-adctl > [data-adstep="-1"] { order: 0; }
.tl-adsheet .tl-adcount { order: 1; min-width: 4.5em; margin: 0; text-align: center; font-size: 14px; }
.tl-adsheet .tl-adctl > [data-adstep="1"] { order: 2; }
.tl-adsheet .tl-adall { order: 3; min-height: 44px; margin-left: auto; padding: 0 14px; font-size: 14px; }
/* "See all": two per row, a heading before each network's first ad, whole pictures on white, a Google ad's headline */
.tl-adsheet .tl-adgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); max-height: none; overflow: visible; padding: 8px 0 14px; }
.tl-adgrid-h { grid-column: 1 / -1; margin-top: 6px; font-size: 13px; font-weight: 700; color: var(--tl-ink2); }
.tl-adgrid-h:first-child { margin-top: 0; }
.tl-adsheet .tl-adgo { background: var(--tl-card); }
.tl-adsheet .tl-adgo img { object-fit: contain; }
.tl-adsheet .tl-adgotxt { -webkit-line-clamp: 6; padding: 12px; font-size: 14px; line-height: 1.3; background: var(--tl-card); }
@media (prefers-reduced-motion: reduce) { .tl-sheet.tl-adsheet { animation: none; } .tl-adsheet .tl-adtrack { transition: none; } }

/* THE PHONE, AFTER THE DESIGN CRITIQUE (1 Oct; three critics on twelve phone screenshots, one ranked list) */
/* the same-day dot names both brands, the left one first, where it was an empty ring */
.tl-app.phone .tl-vdot.same { width: 30px; height: 20px; margin-top: 4px; border: 0; border-radius: 10px; background: var(--tl-canvas); }
.tl-app.phone .tl-vdot.same .tl-sw { width: 16px; height: 16px; }
.tl-app.phone .tl-vdot.same .tl-sw + .tl-sw { margin-left: -4px; }
.tl-app.phone .tl-vrow.has-l .tl-vdot.same::before, .tl-app.phone .tl-vrow.has-r .tl-vdot.same::after { width: 9px; }
/* the centre line's words at the board's own label size, in sentence case */
.tl-app.phone .tl-vsame { font-size: 11px; text-transform: none; letter-spacing: 0; }
.tl-app.phone .tl-vlater { font-size: 11.5px; }
.tl-app.phone .tl-vdate { font-size: 12px; }
/* the level in use reads as chosen: the board's dark blue and yellow, as the chosen filter does (it was grey on white) */
.tl-app.phone .tl-vtools .tl-seg button[aria-pressed="true"] { background: var(--tl-btn-bg); color: var(--tl-btn-fg); }
/* All platforms says so beside its logo */
.tl-plat-short { display: none; }
.tl-app.phone .tl-plat-short { display: inline; font-size: 13px; font-weight: 600; }
/* and on a desk, under All only (each network's own logo names it; All is every screen's default since 15 Oct) */
.tl-app.desk .tl-platbtn.all .tl-plat-short { display: inline; font-size: 13px; font-weight: 600; }
/* the filters fade at the right edge while more are off screen, as on a desk */
.tl-app.phone .tl-vsticky .tl-chips.tl-fade { -webkit-mask-image: linear-gradient(to right, var(--tl-ink) calc(100% - 28px), transparent); mask-image: linear-gradient(to right, var(--tl-ink) calc(100% - 28px), transparent); }
/* the brand search: 16px, or an iPhone zooms the page when it is pressed; the list as wide as the counts */
.tl-app.phone .tl-pick-q { font-size: 16px; height: 44px; }
.tl-app.phone .tl-pickmenu.head { width: calc(100% - 28px); max-width: none; }

/* THE PHONE'S SLIM PINNED AREA (PHONE_SLIM, the version proposed on the Decision Desk, 1 Oct): the pinned row holds the
   platform menu, the level as a small menu and the filters; under it, one slim line of follower counts with each brand's
   menu, "Followers" and the week between them. The four level buttons are put away, not deleted. */
.tl-lvlsel { display: none; }
.tl-app.phone.slim .tl-lvlsel { display: inline-block; flex: none; width: auto; height: 44px; padding: 0 22px 0 8px; font-size: 13px; font-weight: 600; color: var(--tl-ink); }
.tl-app.phone.slim .tl-lvlsel:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: 2px; }
.tl-app.phone.slim .tl-right { gap: 6px; }
.tl-app.phone.slim .tl-vbody > .tl-vtools { display: none; }
.tl-app.phone.slim .tl-vsticky .tl-controls { padding: 6px 0 6px 14px; }
/* (two compact lines: on one, the 390px phone cut both names to "Ope..." and "Anthro...". Each brand's menu with its whole
   name, its count under it, and "Followers" over the week between the two) */
.tl-app.phone.slim .tl-vsticky .tl-vhead { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: start; column-gap: 8px; min-height: 0; margin: 0; padding: 0 14px 6px; border: 0; }
.tl-app.phone.slim .tl-vhead .tl-vh-wk { grid-column: 2; grid-row: 1; align-self: center; margin: 0; white-space: pre-line; text-align: center; font-size: 11px; font-weight: 600; line-height: 1.2; color: var(--tl-ink2); }
.tl-app.phone.slim .tl-vhead .tl-vh-b { grid-row: 1; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.tl-app.phone.slim .tl-vhead .tl-vh-b.l0 { grid-column: 1; align-items: flex-start; }
.tl-app.phone.slim .tl-vhead .tl-vh-b.l1 { grid-column: 3; align-items: flex-end; }
.tl-app.phone.slim .tl-vhead .tl-vh-pick { margin: 0; min-width: 0; max-width: 100%; }
.tl-app.phone.slim .tl-vhead .tl-vh-pick > span:not(.tl-sw) { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tl-app.phone.slim .tl-vhead .tl-vh-val { font-size: 15px; line-height: 1.15; padding: 0 4px; }
.tl-app.phone.slim .tl-vhead .tl-vh-chg, .tl-app.phone.slim .tl-vhead .tl-vh-none { display: none; }

/* ── CALL MINUTES RUN OUT (2026-10-03, Decision Desk call-minutes-topup-packs) ──────────────────────────────────
   "Add minutes" is gold on navy, the site's own pair, so it reads as the way forward and not as another Call. The
   notice strip is roomy on purpose (Rossi on the mockup: "more vertical space between each elements"). */
.tl-call-btn.tl-call-buy { border-color: var(--gold) !important; background: var(--gold) !important; color: var(--navy) !important; text-decoration: none; font-weight: 700; }
.tl-call-btn.tl-call-buy:hover { filter: brightness(1.06); }
.tl-sheet-callrow.quiet .tl-call-who { flex-wrap: wrap; row-gap: 14px; }
.tl-sheet-callrow.quiet .tl-call-buy { margin-left: auto; }
.tl-minnote { position: fixed; z-index: 60; left: 50%; transform: translateX(-50%); bottom: 24px; width: min(560px, calc(100% - 32px)); box-shadow: 0 12px 32px -10px var(--navy-lift); display: flex; align-items: center; flex-wrap: wrap; gap: 12px 18px; margin: 0; padding: 16px 18px; border-radius: 12px; background: var(--gold-soft); border: 1px solid var(--gold); color: var(--navy); font-size: 14.5px; line-height: 1.5; }
.tl-minnote.out { border-width: 2px; }
.tl-minnote-t { flex: 1 1 220px; font-weight: 500; }
.tl-minnote-a { flex: 0 0 auto; display: inline-flex; align-items: center; min-height: 40px; padding: 0 16px; border-radius: 999px; background: var(--navy); color: var(--gold); font-weight: 700; text-decoration: none; }
.tl-minnote-a:hover { background: var(--navy-hover); }
.tl-minnote-x { flex: 0 0 auto; width: 40px; height: 40px; display: inline-flex; align-items: center; justify-content: center; border: 0; border-radius: 50%; background: transparent; color: var(--navy); cursor: pointer; }
.tl-minnote-x:hover { background: var(--navy-hint); }
.tl-minnote-x svg { width: 18px; height: 18px; }
@media (max-width: 768px) { .tl-minnote { bottom: calc(80px + env(safe-area-inset-bottom)); } }
body:not(.view-timeline) .tl-minnote.on-board { display: none; }
/* design critique, 3 Oct: on a phone the bottom bar already turns into a gold "Add minutes" when none are left; a second
   button right above it competed with it, so the notice keeps only its words there */
@media (max-width: 768px) { .tl-minnote.out.on-board .tl-minnote-a { display: none; } }
@media (max-width: 768px) {
  /* The phone's call bar sits ON the bottom nav, with no strip of the list showing between them (Rossi, 3 Oct, a
     screenshot with two red arrows: "That navy blue panel should be glued to the sticky bottom nav"). Pinned to the
     list's foot it stopped 16px short (#main's own bottom padding) and up to 40px at the end of the list (the items
     after it). So on the board it is fixed to the nav's top edge, and the list keeps room for it while it shows. */
  html.mnav-fixed body.view-timeline #timeline-host .tl-callbar { position: fixed; left: 0; right: 0; bottom: calc(var(--mnav-h) + env(safe-area-inset-bottom)); margin: 0; padding-bottom: 8px; z-index: 40; }
  html.mnav-fixed body.view-timeline #timeline-host .tl-app:has(> .tl-callbar:not([hidden])) { padding-bottom: 76px; }
  /* Rossi, 13 Oct, two red arrows: with a post ticked during a call, the "posts ticked" bar peeked out above the call
     bar, 42px of a narrower navy pill, its Clear under the call bar. It now stands 10px above the call bar. Its sticky
     stop already sits inside #main's 16px bottom padding, hence the minus 16px. */
  html.mnav-fixed body.view-timeline #timeline-host .tl-app.callbar-on .tl-pickbar { bottom: calc(var(--tl-cb-h, 60px) - 16px + 10px); }
}
/* the same on the timeline's own page, where nothing pads the list's foot */
.tl-app.callbar-on .tl-pickbar { bottom: calc(var(--tl-cb-h, 60px) + 10px); }
/* a phone: one tidy two-line bar the width of the call bar, never a pill that wraps into a ragged shape. The count on
   line 1, what to do next on line 2, Clear on the right across both (measured at 360 to 430px, 13 Oct) */
.tl-app.phone .tl-pickbar { display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: 12px; row-gap: 1px; align-items: center;
  width: auto; max-width: none; margin: 12px -2px 0; padding: 8px 8px 8px 16px; border-radius: 14px; }
.tl-app.phone .tl-pickbar[hidden] { display: none; }
.tl-app.phone .tl-pickbar-n { grid-column: 1; grid-row: 1; }
.tl-app.phone .tl-pickbar-hint { grid-column: 1; grid-row: 2; font-size: 12.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tl-app.phone .tl-pickbar-x { grid-column: 2; grid-row: 1 / span 2; min-height: 44px; display: inline-flex; align-items: center; box-sizing: border-box; }

/* ══ NEWS AND KEY DATES (2026-10-04, the Trafigura board: "Show the news and dates in the Trafigura board's timeline") ══
   Only a board whose timeline profile reads news (timeline.js PROFILES, news: true) ever draws these classes; every other
   board's timeline is untouched. A news card says who reported it where a post says its reactions (never "0 likes"),
   and leads with a newspaper where a post leads with its network's logo. A key date is not a moment: a label beside the
   line after Today (wide), and a "Coming up" block after Today (the list). An EXPECTED date is drawn tentative (dashed,
   quieter) and says "Expected"; a CONFIRMED one is solid and says "Confirmed". Colours are board variables only (T1). */
.tl-src { display: inline-flex; align-items: center; gap: 5px; min-width: 0; max-width: 100%; overflow: hidden; white-space: nowrap; color: var(--tl-ink2); }
/* the words in their own box: a bare text node beside the icon is an anonymous flex item, which never shows "…" (review,
   4 Oct: "Company release, trafigura.com" was cut mid-word in a 138px box) */
.tl-src-t { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tl-src-ic { width: 13px; height: 13px; flex: none; color: var(--tl-ink3); }
.tl-vsrc { display: flex; align-items: center; gap: 5px; color: var(--tl-ink2); }
.tl-vcard.news .tl-vbot { font-weight: 500; }
.tl-pi.news { color: var(--tl-ink2); }

/* the source, opened (the sheet, the right panel, the phone's player) */
.tl-newsbox { margin: 12px 0 0; font-size: 13.5px; color: var(--tl-ink2); }
.tl-newsbox p { margin: 0; }
.tl-news-src { display: flex; align-items: center; gap: 6px; font-weight: 600; color: var(--tl-ink); }
.tl-news-more { margin-top: 6px !important; }
.tl-news-list { display: flex; flex-wrap: wrap; gap: 4px 12px; margin: 4px 0 0; padding: 0; list-style: none; }
.tl-news-list a { color: var(--tl-ink); text-decoration: underline; text-underline-offset: 2px; }
.tl-call.is-navy .tl-news-list a { color: var(--navy-link); text-decoration-color: var(--navy-link-line); }

/* key dates on the wide timeline: the label, its stem to the line, the days a multi-day date covers, its mark */
.tl-kd { position: absolute; box-sizing: border-box; display: flex; flex-direction: column; justify-content: center; gap: 1px; padding: 4px 10px; overflow: hidden;
  background: var(--tl-card); border: 1.5px solid var(--tl-rule2); border-radius: 8px; font-size: 11.5px; line-height: 1.25; color: var(--tl-ink); pointer-events: none; }
.tl-kd.l0.confirmed { border-color: var(--tl-lane-0); }
.tl-kd.l1.confirmed { border-color: var(--tl-lane-1); }
.tl-kd.expected { border-style: dashed; background: var(--tl-canvas); color: var(--tl-ink2); }
.tl-kd-when { white-space: nowrap; color: var(--tl-ink2); }
.tl-kd-when b { font-weight: 700; color: var(--tl-ink); }
.tl-kd-t { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-weight: 600; }
.g-kdstem { stroke-width: 1.5; }
.g-kdstem.l0 { stroke: var(--tl-lane-0); }
.g-kdstem.l1 { stroke: var(--tl-lane-1); }
.g-kdstem.expected, .g-kdspan.expected { stroke-dasharray: 4 3; }
.g-kdspan { stroke-width: 3; stroke-linecap: round; opacity: .55; }
.g-kdspan.l0 { stroke: var(--tl-lane-0); }
.g-kdspan.l1 { stroke: var(--tl-lane-1); }
.tl-kdmark { position: absolute; width: 12px; height: 12px; box-sizing: border-box; transform: rotate(45deg); background: var(--tl-canvas); border: 2px solid var(--tl-ink2); pointer-events: auto; cursor: default; }
.tl-kdmark.l0 { border-color: var(--tl-lane-0); }
.tl-kdmark.l1 { border-color: var(--tl-lane-1); }
.tl-kdmark.confirmed.l0 { background: var(--tl-lane-0); }
.tl-kdmark.confirmed.l1 { background: var(--tl-lane-1); }
.tl-kdmark.expected { border-style: dashed; }

/* key dates in the list: "Coming up" after Today, each date in its company's column with the date on the line */
.tl-vmark.upcoming > span { border: 1px dashed var(--tl-rule2); border-radius: 999px; background: var(--tl-card); color: var(--tl-ink2); }
.tl-vkd { position: relative; display: grid; grid-template-columns: minmax(0, 1fr) 48px minmax(0, 1fr); align-items: start; margin: 0 -4px; padding: 10px 4px 4px; }
.tl-vkd .tl-vgut { min-height: 40px; }
.tl-vkd-dot { position: relative; z-index: 1; margin-top: 5px; width: 12px; height: 12px; box-sizing: border-box; transform: rotate(45deg); background: var(--tl-canvas); border: 2px solid var(--tl-ink2); }
.tl-vkd.l0 .tl-vkd-dot { border-color: var(--tl-lane-0); }
.tl-vkd.l1 .tl-vkd-dot { border-color: var(--tl-lane-1); }
.tl-vkd.confirmed.l0 .tl-vkd-dot { background: var(--tl-lane-0); }
.tl-vkd.confirmed.l1 .tl-vkd-dot { background: var(--tl-lane-1); }
.tl-vkd.expected .tl-vkd-dot { border-style: dashed; }
.tl-vkdcard { box-sizing: border-box; display: flex; flex-direction: column; gap: 4px; min-width: 0; padding: 8px 10px; border: 1.5px solid var(--tl-rule2); border-radius: 10px; background: var(--tl-card); color: var(--tl-ink); overflow-wrap: anywhere; }
.tl-vkdcard.confirmed.l0 { border-color: var(--tl-lane-0); }
.tl-vkdcard.confirmed.l1 { border-color: var(--tl-lane-1); }
.tl-vkdcard.expected { border-style: dashed; background: var(--tl-canvas); }
.tl-vkd-top { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.tl-vkd-conf { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; color: var(--tl-ink2); }
.tl-vkdcard.confirmed .tl-vkd-conf { color: var(--tl-ink); }
.tl-vkd-t { font-size: 13px; font-weight: 600; line-height: 1.25; }
.tl-vkdcard.expected .tl-vkd-t { font-weight: 500; }
.tl-vkd-src { align-self: flex-start; font-size: 12px; color: var(--tl-ink2); text-decoration: underline; text-underline-offset: 2px; }
.tl-vkd-src:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: 2px; }

/* THE PHONE'S PINNED COUNTS (Rossi, 8 Oct: the brand menus and follower counts are part of the pinned block). Pinned,
   "Followers, week of 6 Jul" on a line of its own made the block 171px, a fifth of a 390x844 phone (design critique,
   8 Oct): the label sits between the two brand menus instead, on two short lines, each brand's menu with its count and
   change under it, so the counts cost one row, not two. */
.tl-app.phone:not(.slim) .tl-vsticky .tl-vhead { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: start; column-gap: 8px; min-height: 0; margin: 0; padding: 0 14px 8px; border: 0; }
.tl-app.phone:not(.slim) .tl-vsticky .tl-vhead .tl-vh-wk { grid-column: 2; grid-row: 1; align-self: center; margin: 0; white-space: pre-line; text-align: center; font-size: 11px; font-weight: 600; line-height: 1.25; color: var(--tl-ink2); }
.tl-app.phone:not(.slim) .tl-vsticky .tl-vhead .tl-vh-b { grid-row: 1; display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.tl-app.phone:not(.slim) .tl-vsticky .tl-vhead .tl-vh-b.l0 { grid-column: 1; align-items: flex-start; }
.tl-app.phone:not(.slim) .tl-vsticky .tl-vhead .tl-vh-b.l1 { grid-column: 3; align-items: flex-end; text-align: right; }
.tl-app.phone:not(.slim) .tl-vsticky .tl-vhead .tl-vh-pick { margin: 0 0 2px; min-width: 0; max-width: 100%; }
.tl-app.phone:not(.slim) .tl-vsticky .tl-vhead .tl-vh-pick > span:not(.tl-sw) { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tl-app.phone:not(.slim) .tl-vsticky .tl-vhead .tl-vh-val { font-size: 17px; line-height: 1.15; padding: 0 2px; }
.tl-app.phone:not(.slim) .tl-vsticky .tl-vhead .tl-vh-chg { min-height: 0; font-size: 11px; padding: 0 2px; }

/* THE ADS CHIP (Rossi, 8 Oct: an Ads option among the filters, on every board). The wide horizontal view never draws
   ads, so it does not offer the chip. */
.tl-app:not(.vertical) .tl-chip.ads, .tl-app:not(.vertical) .tl-chip.aivis { display: none; }

/* THE METRICS ROW (Rossi, 8 Oct: every count a network keeps, as on the board's post cards, icon and short number,
   "to save horizontal space"; and the size "adjusted automatically for desktop, tablet, mobile"). One line of icon +
   number pairs that wraps whole pairs, never inside one. Sizes per layout (design critique, 8 Oct): a desk card is
   about 260px of text column, a phone card about 160px, so the desk gets 12.5px numbers and 14px icons, the phone
   11.5px and 13px with tighter gaps, which still fits X's six counts on two lines at 390px. The likes count, the one
   the key moments are measured on, is the one in ink; the rest are one step quieter. */
.tl-mets { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; font-size: 12.5px; line-height: 1.3; color: var(--tl-ink2); font-variant-numeric: tabular-nums; }
.tl-met { display: inline-flex; align-items: center; gap: 3px; white-space: nowrap; }
.tl-met svg { width: 14px; height: 14px; flex: none; color: var(--tl-ink3); }
.tl-met.likes { color: var(--tl-ink); font-weight: 700; }
.tl-met.likes svg { color: var(--tl-ink2); }
.tl-vcard.big .tl-mets { font-weight: 500; }
.tl-app.phone .tl-mets { gap: 3px 8px; font-size: 10.5px; }
.tl-app.phone .tl-met { gap: 2px; }
.tl-app.phone .tl-met svg { width: 13px; height: 13px; }
@media (min-width: 1600px) { .tl-app.desk .tl-mets { font-size: 13px; } .tl-app.desk .tl-met svg { width: 15px; height: 15px; } }
/* Air above the counts row so it reads as the card's footer rather than another line of the headline (Rossi, 8 Oct).
   The desk rule has to out-rank .tl-vcard.desk .tl-vbot's own padding-top, which is why it names both classes. */
.tl-mets { padding-top: 12px; }
.tl-vcard.desk .tl-vbot.tl-mets { padding-top: 20px; }

/* A CARD THE READER ASKED FOR, on their own timeline (2026-10-10). It wears the board's own amber, the colour the
   second lane and the card itself already use, and is never shaped like a post: a card is something the reader made
   during a call, not a moment a brand made. Only this reader ever sees one (the cards door decides that from their
   sign-in). No colour literal here: every value is one of the roles defined at the top of this file. */
.tl-vcardpin { position: relative; display: grid; grid-template-columns: minmax(0, 1fr) 48px minmax(0, 1fr); align-items: start; margin: 0 -4px; padding: 10px 4px 4px; }
.tl-vcardpin .tl-vgut { min-height: 40px; }
.tl-vcard-dot { position: relative; z-index: 1; margin-top: 5px; width: 12px; height: 12px; box-sizing: border-box; border-radius: 50%; background: var(--tl-lane-1); border: 2px solid var(--tl-canvas); }
.tl-vcardcard { display: flex; flex-direction: column; gap: 6px; width: 100%; text-align: left; padding: 10px 12px; border: 1px solid var(--tl-lane-1); border-radius: 10px; background: var(--tl-card); color: inherit; cursor: pointer; font: inherit; }
.tl-vcardcard:hover { background: var(--tl-tag); }
/* THE BOARD'S WEEKLY AI VISIBILITY READING (Rossi, 15 Oct: "increase its border width and propose a bright color so that
   it stands out a bit more"; relabelled "Weekly AI Visibility" with the AI answers icon of the board's left panel). A
   3px violet frame (2px more than a card of the reader's, so its padding is 2px less and its words line up with theirs),
   and its label filled in the same violet. */
.tl-vcardcard.weekly { border: 3px solid var(--tl-aivis); padding: 8px 10px; }
.tl-vcardcard.weekly:hover { background: var(--tl-aivis-soft); }
.tl-vcardcard.weekly .tl-tag { display: inline-flex; align-items: center; gap: 5px; background: var(--tl-aivis); color: var(--tl-on-aivis); font-weight: 600; }
.tl-vcardcard.weekly .tl-tag svg { width: 13px; height: 13px; flex: none; }
/* the card just made, ringed for a few seconds once it is closed, so the reader sees where it was kept (14 Oct) */
.tl-vcardcard.tl-justmade { box-shadow: 0 0 0 3px var(--tl-lane-1); animation: tl-justmade 1s ease-in-out 3; }
.tl-cardmark.tl-justmade { box-shadow: 0 0 0 4px var(--tl-canvas), 0 0 0 7px var(--tl-lane-1); animation: tl-justmade 1s ease-in-out 3; }
@keyframes tl-justmade { 50% { box-shadow: 0 0 0 1px var(--tl-lane-1); } }
@media (prefers-reduced-motion: reduce) { .tl-vcardcard.tl-justmade, .tl-cardmark.tl-justmade { animation: none; } }
.tl-vcard-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.tl-vcard-when { font-size: 11.5px; color: var(--tl-ink2); font-variant-numeric: tabular-nums; }
.tl-vcard-q { font-weight: 600; line-height: 1.3; }
.tl-vcard-go { font-size: 12px; font-weight: 600; color: var(--tl-ink2); }
/* THE CARD'S CHART UNDER ITS QUESTION (Rossi, 12 Oct, replacing the post photos of the first build: "the card should
   show a part of the chart"). The card's first chart, small, on the card's own navy: the figures with their bars, the
   line, the pie, the blocks. Drawn by assets/timeline-cards/card_thumb.js from the card's counted numbers. */
.tl-th { display: flex; flex-direction: column; gap: 6px; padding: 12px 14px; border-radius: 8px; background: var(--tl-th-bg); color: var(--tl-th-ink); min-width: 0; container-type: inline-size; }
.tl-th-cap { font-size: 11.5px; font-weight: 600; color: var(--tl-th-ink2); }
.tl-th-row { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: baseline; column-gap: 8px; row-gap: 4px; }
.tl-th-num { font-size: 22px; font-weight: 800; line-height: 1.05; letter-spacing: -.01em; font-variant-numeric: tabular-nums; }
.tl-th-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12.5px; font-weight: 600; color: var(--tl-th-ink); }
.tl-th-bar { grid-column: 1 / -1; display: block; height: 5px; border-radius: 3px; background: var(--tl-th-track); overflow: hidden; }
.tl-th-bar i { display: block; height: 100%; border-radius: 3px; }
.tl-th-svg { display: block; width: 100%; height: 72px; overflow: visible; }
.tl-th-strip { height: 40px; }
.tl-th-axis { stroke: var(--tl-th-track); stroke-width: 1.5; }
.tl-th-ln { fill: none; stroke-width: 2.5; stroke-linejoin: round; stroke-linecap: round; }
.tl-th-xl { display: flex; justify-content: space-between; font-size: 11px; color: var(--tl-th-ink2); }
.tl-th-key { display: flex; flex-wrap: wrap; gap: 4px 14px; font-size: 12px; color: var(--tl-th-ink2); }
.tl-th-key > span { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
.tl-th-key b { color: var(--tl-th-ink); font-weight: 700; }
.tl-th-sw { flex: none; width: 8px; height: 8px; border-radius: 50%; }
.tl-th-pie { display: grid; grid-template-columns: 76px minmax(0, 1fr); gap: 14px; align-items: center; }
/* the pie's key: a swatch, a name and its share, the shares in one column right after the longest name (never pushed
   to the far edge, where a share sat 200px from its own name on a wide card) */
.tl-th-pie .tl-th-key { display: grid; grid-template-columns: auto minmax(0, max-content) auto; justify-content: start; align-items: center; column-gap: 8px; row-gap: 4px; }
.tl-th-pie .tl-th-key > span { display: contents; }
.tl-th-donut { width: 76px; height: 76px; transform: none; }
.tl-th-slice { fill: none; stroke-width: 7; }
/* the colours: a brand keeps its own (white, gold, then ADL's for a third brand on), a part takes them in size order */
.tl-th-lav { background: var(--tl-th-lav); fill: var(--tl-th-lav); } .tl-th-mint { background: var(--tl-th-mint); fill: var(--tl-th-mint); }
.tl-th-cyan { background: var(--tl-th-cyan); fill: var(--tl-th-cyan); } .tl-th-rose { background: var(--tl-th-rose); fill: var(--tl-th-rose); }
.tl-th-violet { background: var(--tl-th-violet); fill: var(--tl-th-violet); } .tl-th-blue { background: var(--tl-th-blue); fill: var(--tl-th-blue); }
.tl-th-base { background: var(--tl-th-base); fill: var(--tl-th-base); }
.tl-tx-lav { color: var(--tl-th-lav); } .tl-tx-mint { color: var(--tl-th-mint); } .tl-tx-cyan { color: var(--tl-th-cyan); }
.tl-tx-rose { color: var(--tl-th-rose); } .tl-tx-violet { color: var(--tl-th-violet); } .tl-tx-blue { color: var(--tl-th-blue); } .tl-tx-base { color: var(--tl-th-ink); }
/* a brand's logo before its name (Rossi, 12 Oct): round, on a light disc so a dark or see-through logo still reads on
   the navy; in a colour key it stands in for the dot, so it wears a ring of that colour */
.tl-th-lg { display: inline-block; flex: none; width: 16px; height: 16px; margin-right: 6px; border-radius: 50%; object-fit: cover; background: var(--tl-th-ink); vertical-align: -3px; }
.tl-th-key .tl-th-lg { margin-right: 0; }
.tl-th-rg { width: 14px; height: 14px; margin: 2px; }
.tl-th-rg-lav { box-shadow: 0 0 0 2px var(--tl-th-lav); } .tl-th-rg-mint { box-shadow: 0 0 0 2px var(--tl-th-mint); } .tl-th-rg-cyan { box-shadow: 0 0 0 2px var(--tl-th-cyan); }
.tl-th-rg-rose { box-shadow: 0 0 0 2px var(--tl-th-rose); } .tl-th-rg-violet { box-shadow: 0 0 0 2px var(--tl-th-violet); } .tl-th-rg-blue { box-shadow: 0 0 0 2px var(--tl-th-blue); }
.tl-th-rg-base { box-shadow: 0 0 0 2px var(--tl-th-base); }
.tl-st-lav { stroke: var(--tl-th-lav); } .tl-st-mint { stroke: var(--tl-th-mint); } .tl-st-cyan { stroke: var(--tl-th-cyan); }
.tl-st-rose { stroke: var(--tl-th-rose); } .tl-st-violet { stroke: var(--tl-th-violet); } .tl-st-blue { stroke: var(--tl-th-blue); } .tl-st-base { stroke: var(--tl-th-base); }
/* a phone's list column is about 160px: each name goes under its figure, as on the card itself, instead of being cut
   to "Anth..." beside it, and the pie's key goes under the pie (screenshot review, 12 Oct). LAST in this block on
   purpose: a container rule adds no weight, so above the rules it changes it lost to them and did nothing. */
@container (max-width: 260px) {
  .tl-th-row { grid-template-columns: minmax(0, 1fr); row-gap: 2px; }
  .tl-th-num { font-size: 20px; }
  .tl-th-bar { margin-top: 3px; }
  .tl-th-pie { grid-template-columns: minmax(0, 1fr); justify-items: start; gap: 10px; }
  .tl-th-pie .tl-th-key { width: 100%; }
}
/* ...and on the wide timeline, a mark on the line itself at the minute the card was asked for. */
.tl-cardmark { pointer-events: auto; position: absolute; width: 18px; height: 18px; padding: 0; border-radius: 50%; border: 2px solid var(--tl-canvas); background: var(--tl-lane-1); cursor: pointer; z-index: 3; }
.tl-cardmark::after { content: ""; position: absolute; inset: 4px; border-radius: 2px; background: var(--tl-canvas); }
.tl-cardmark:hover, .tl-cardmark:focus-visible { transform: scale(1.15); }
/* A mark with no name is a gold dot nobody can explain on a later visit, so hovering or tabbing to it says what it
   is. Drawn by the mark itself rather than placed by the layout, so it can never push a post's card out of the way. */
.tl-cardmark::before { content: "Your card"; position: absolute; left: 50%; bottom: calc(100% + 7px); transform: translateX(-50%); padding: 3px 8px; border-radius: 999px; border: 1px solid var(--tl-lane-1); background: var(--tl-card); color: var(--tl-ink); font: 600 11.5px/1.25 inherit; white-space: nowrap; opacity: 0; pointer-events: none; transition: opacity .12s; }
.tl-cardmark:hover::before, .tl-cardmark:focus-visible::before { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .tl-cardmark::before { transition: none; } .tl-cardmark:hover, .tl-cardmark:focus-visible { transform: none; } }

/* ── POSTS TICKED TO COMPARE (Rossi, 12 Oct: "checkboxes at the top right corner of the cards ... only appear if one
   card is selected, and not appear by default on pageload"). Nothing on load. A computer shows a post's tick under
   the pointer or the keyboard; once one post is ticked (.picking) every post shows its own. A phone shows the ticks
   only once one post is ticked (from its sheet), and then a tap ticks. Gold is the one colour for "chosen". ── */
/* 23px on a computer (Rossi, 12 Oct: "on desktop screens, set the size of this check box to 23px"; it was 28) */
.tl-vpick { position: absolute; top: 8px; right: 8px; z-index: 3; width: 23px; height: 23px; padding: 0; display: grid; place-items: center; border-radius: 6px;
  border: 1.5px solid var(--tl-rule2); background: var(--tl-card); color: transparent; cursor: pointer; opacity: 0; transition: opacity .15s ease; box-shadow: 0 1px 3px var(--tl-shadow); }
.tl-vpick svg { width: 14px; height: 14px; }
.tl-vitem:hover .tl-vpick, .tl-vpick:focus-visible, .tl-vlist.picking .tl-vpick, .tl-vpick.on { opacity: 1; }
.tl-vpick:hover { border-color: var(--tl-btn-fg); color: var(--tl-ink3); }
.tl-vpick.on { border-color: var(--gold); background: var(--gold); color: var(--navy); }
.tl-vitem:has(.tl-vpick.on) { border-color: var(--gold); box-shadow: 0 0 0 1px var(--gold); }
/* a phone: a badge inside the card (a card is one button, and a button cannot hold another) */
.tl-vcard { position: relative; }
.tl-vpick-b { display: none; position: absolute; top: 6px; right: 6px; z-index: 2; width: 22px; height: 22px; place-items: center; border-radius: 6px;
  border: 1.5px solid var(--tl-rule2); background: var(--tl-card); color: transparent; }
.tl-vpick-b svg { width: 14px; height: 14px; }
.tl-vlist.picking .tl-vpick-b { display: grid; }
.tl-vpick-b.on { border-color: var(--gold); background: var(--gold); color: var(--navy); }
.tl-vcard.picked { border-color: var(--gold); box-shadow: 0 0 0 1px var(--gold); }
/* the phone sheet's way in: "Compare with another post" */
.tl-sheet-pick { all: unset; box-sizing: border-box; display: inline-flex; align-items: center; gap: 8px; margin-top: 14px; min-height: 44px; padding: 0 14px; border-radius: 999px;
  border: 1px solid var(--tl-rule2); background: var(--tl-pick-bg); color: var(--tl-ink); font-size: 14px; font-weight: 600; cursor: pointer; }
/* 13 Oct: on the light blue the grey box outline was 1.3:1 and hard to see as a box to tick; the muted ink is 4:1 */
.tl-sheet-pick svg { width: 16px; height: 16px; padding: 2px; border-radius: 5px; border: 1.5px solid var(--tl-ink3); color: transparent; }
.tl-sheet-pick.on svg { background: var(--gold); border-color: var(--gold); color: var(--navy); }
.tl-sheet-pick:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: 2px; }
/* the bar: how many are ticked, what to do next, and a way to clear them, pinned to the foot of the list */
.tl-pickbar { position: sticky; bottom: calc(14px + env(safe-area-inset-bottom)); z-index: 6; display: flex; align-items: center; flex-wrap: wrap; gap: 6px 14px;
  width: max-content; max-width: calc(100% - 24px); margin: 12px auto 0; padding: 10px 10px 10px 18px; border-radius: 999px;
  background: var(--navy); color: var(--on-navy); box-shadow: 0 6px 20px var(--tl-shadow); font-size: 13.5px; }
.tl-pickbar[hidden] { display: none; }
.tl-pickbar-n b { color: var(--gold); font-variant-numeric: tabular-nums; }
.tl-pickbar-hint { color: var(--on-navy-2); }
.tl-pickbar-x { all: unset; cursor: pointer; padding: 6px 14px; border-radius: 999px; background: var(--on-navy); color: var(--navy); font-weight: 600; }
.tl-pickbar-x:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
/* the tick sits over the card's top line: keep its date, type and "Repost" clear of it */
.tl-vitem:has(.tl-vpick) .tl-vtop { padding-right: 31px; }
.tl-vlist.picking .tl-vcard .tl-vtop { padding-right: 26px; }

/* "Card deleted" (16 Oct, Rossi: "add/display a toast notification confirming the post being deleted"). A portal on
   <body>, so it sits above the board's panels and the phone's sheet; bottom centre, the board's navy and gold. */
.tl-toast { position: fixed; left: 50%; bottom: 24px; z-index: 2147483000; transform: translate(-50%, 12px);
  max-width: min(92vw, 480px); padding: 12px 18px; border-radius: 10px; background: var(--navy); color: var(--on-navy);
  font: 600 14px/1.4 var(--font-body, inherit); box-shadow: 0 6px 20px var(--tl-shadow); text-align: center;
  opacity: 0; pointer-events: none; transition: opacity .2s ease, transform .2s ease; }
.tl-toast::before { content: ""; display: inline-block; width: 8px; height: 8px; margin-right: 10px; border-radius: 50%;
  background: var(--tl-btn-fg); vertical-align: 1px; }
.tl-toast.on { opacity: 1; transform: translate(-50%, 0); }
@media (prefers-reduced-motion: reduce) { .tl-toast { transition: none; } }

/* ═══ ONE GENEVA LINE (GenevAI, Rossi, 24 Oct: "Alternating sides, in date order") ═════════════════════════════════
   No places to compare: the two columns are a layout, and a card's colour says its KIND. Each kind re-points the two
   lane colours at its own, so every rule above that draws a lane's edge, dot, stem or ring draws the kind instead (on
   the list, the phone's sheet and the call panel alike: the k- classes are only ever written on one line). */
.k-event   { --tl-lane-0: var(--kind-event);   --tl-lane-1: var(--kind-event);   --tl-kind-ink: var(--kind-event-ink);   --tl-kind-soft: var(--kind-event-soft); }
.k-news    { --tl-lane-0: var(--kind-news);    --tl-lane-1: var(--kind-news);    --tl-kind-ink: var(--kind-news-ink);    --tl-kind-soft: var(--kind-news-soft); }
.k-listing { --tl-lane-0: var(--kind-listing); --tl-lane-1: var(--kind-listing); --tl-kind-ink: var(--kind-listing-ink); --tl-kind-soft: var(--kind-listing-soft); }
.k-menu    { --tl-lane-0: var(--kind-menu);    --tl-lane-1: var(--kind-menu);    --tl-kind-ink: var(--kind-menu-ink);    --tl-kind-soft: var(--kind-menu-soft); }
.k-deal    { --tl-lane-0: var(--kind-deal);    --tl-lane-1: var(--kind-deal);    --tl-kind-ink: var(--kind-deal-ink);    --tl-kind-soft: var(--kind-deal-soft); }
.k-mix     { --tl-lane-0: var(--tl-ink2);      --tl-lane-1: var(--tl-ink2); }
/* (28 Oct) an event's TYPE, written after k-event so it wins: concerts, exhibitions, shows, family, sport and nature,
   fairs and markets, and anything else (the type chip's own key, kindCls) */
.g-concerts    { --tl-lane-0: var(--genre-concerts);    --tl-lane-1: var(--genre-concerts);    --tl-kind-ink: var(--genre-concerts-ink);    --tl-kind-soft: var(--genre-concerts-soft); }
.g-expositions { --tl-lane-0: var(--genre-expositions); --tl-lane-1: var(--genre-expositions); --tl-kind-ink: var(--genre-expositions-ink); --tl-kind-soft: var(--genre-expositions-soft); }
.g-spectacles  { --tl-lane-0: var(--genre-spectacles);  --tl-lane-1: var(--genre-spectacles);  --tl-kind-ink: var(--genre-spectacles-ink);  --tl-kind-soft: var(--genre-spectacles-soft); }
.g-famille     { --tl-lane-0: var(--genre-famille);     --tl-lane-1: var(--genre-famille);     --tl-kind-ink: var(--genre-famille-ink);     --tl-kind-soft: var(--genre-famille-soft); }
.g-sport       { --tl-lane-0: var(--genre-sport);       --tl-lane-1: var(--genre-sport);       --tl-kind-ink: var(--genre-sport-ink);       --tl-kind-soft: var(--genre-sport-soft); }
.g-fetes       { --tl-lane-0: var(--genre-fetes);       --tl-lane-1: var(--genre-fetes);       --tl-kind-ink: var(--genre-fetes-ink);       --tl-kind-soft: var(--genre-fetes-soft); }
.g-autre       { --tl-lane-0: var(--genre-autre);       --tl-lane-1: var(--genre-autre);       --tl-kind-ink: var(--genre-autre-ink);       --tl-kind-soft: var(--genre-autre-soft); }
/* every card wears its kind on its top edge (not only the big ones), and its tag in the kind's tint */
.tl-app.one .tl-vitem, .tl-app.one .tl-vcard:not(.desk):not(.ms) { border-top: 3px solid var(--tl-lane-0); }
.tl-app.one .tl-vitem:hover { border-top-color: var(--tl-lane-0); }
.tl-app.one .tl-vitem .tl-tag, .tl-app.one .tl-vcard .tl-tag { background: var(--tl-kind-soft); color: var(--tl-kind-ink); }
.tl-app.one .tl-vdot .tl-sw { background: var(--tl-lane-0); }
/* the place: a tag that narrows the line to it (the list's click handler reads data-place) */
.tl-vplace { display: inline-flex; align-items: center; gap: 3px; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11.5px; font-weight: 500; color: var(--tl-ink2); }
.tl-vplace::before { content: ''; flex: none; width: 6px; height: 6px; border-radius: 50%; border: 1.5px solid currentColor; }
.tl-vplace[data-place] { cursor: pointer; text-decoration: underline; text-decoration-color: var(--tl-rule2); text-underline-offset: 3px; }
.tl-vplace[data-place]:hover { color: var(--tl-ink); text-decoration-color: currentColor; }
.tl-vcard.desk .tl-vplace { margin-left: 2px; }   /* beside its genre, never pushed to the far edge (layout audit, 24 Oct: up to 317px apart) */
/* the place the line is narrowed to, in the top band: pressed like a chosen chip, with its way back */
/* (outlined, not filled: beside a chosen chip, which is filled, it reads as a different thing, the place, not a type) */
.tl-placex { display: inline-flex; align-items: center; gap: 6px; flex: none; height: 36px; padding: 0 10px 0 12px; border-radius: 999px; border: 1.5px solid var(--tl-ctl); background: var(--tl-card); color: var(--tl-ctl); font: inherit; font-size: 13px; font-weight: 700; white-space: nowrap; cursor: pointer; }
.tl-placex::before { content: ''; flex: none; width: 7px; height: 7px; border-radius: 50%; border: 2px solid currentColor; }
.tl-placex:hover { background: var(--tl-tag); }
.tl-placex svg { width: 14px; height: 14px; }
.tl-placex:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: 2px; }
.tl-app.phone .tl-placex { min-height: 44px; }
.tl-placebtn { all: unset; box-sizing: border-box; display: inline-flex; align-items: center; min-height: 32px; margin-left: auto; padding: 0 12px; border-radius: 999px; border: 1px solid var(--tl-rule2); background: var(--tl-card); color: var(--tl-ctl); font-size: 12.5px; font-weight: 600; white-space: nowrap; cursor: pointer; }
.tl-placebtn:hover { border-color: var(--tl-ctl); }
.tl-placebtn:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: 2px; }
.tl-call .tl-placebtn { border-color: var(--navy-rule2); background: transparent; color: var(--on-navy); }
.tl-vplacebar { display: flex; justify-content: center; padding: 8px 14px 10px; }
.tl-vplacebar[hidden], .tl-placebtn[hidden] { display: none; }   /* (its own display above would otherwise beat the hidden attribute) */
/* (29 Oct) an opened card's "Seulement ici": a pin and two words, so it sits on the row of the place's name and its kind
   (Rossi: "reduce the size of the element ... so it fits in the same row"). A card with a picture has its close button
   over the picture, so its row no longer keeps the button's room at the right */
.tl-placebtn { gap: 5px; padding: 0 10px 0 8px; }
.tl-placebtn .tl-pin { width: 13px; height: 13px; flex: none; }
.tl-sheet.tall .tl-sheet-meta { padding-right: 0; }

/* (29 Oct) THE CHIP IN USE AND ITS EXTRAS, ONE PILL (Rossi: "expanding the filter framed in yellow horizontally in a
   visually esthetic way to display as follow: Immobilier [mapIcon] [selectedAreas]"). The extras continue the chip's own
   dark fill after a thin divider: the Homes chip's "À vendre / À louer" switch, then the place with its ×. The band's gap
   is a variable so the extras can close it exactly, whatever the band's spacing on that screen */
.tl-chips { --tl-chipgap: 6px; }
.tl-app.desk .tl-vtools .tl-chips { --tl-chipgap: 4px; }
.tl-app.one.desk .tl-vtools .tl-chips { --tl-chipgap: 3px; }
.tl-chip.has-ext { border-top-right-radius: 0; border-bottom-right-radius: 0; padding-right: 10px; }
.tl-chipext { position: relative; flex: none; align-self: stretch; display: inline-flex; align-items: center; gap: 2px; margin-left: calc(-1 * var(--tl-chipgap)); padding: 0 4px 0 7px; border: 1px solid var(--tl-btn-bg); border-left: 0; border-radius: 0 999px 999px 0; background: var(--tl-btn-bg); color: var(--tl-btn-fg); }
.tl-chipext::before { content: ''; position: absolute; left: 0; top: 28%; bottom: 28%; width: 1px; background: currentColor; opacity: .35; }
/* (29 Oct, second pass) Rossi on the switch: "I dont like the design ... I would go more for the blue like the map icon in
   the top nav bar". The extras now speak the bar's own cyan (--gv-accent, the Places pin): a track outlined in it, the two
   words in soft white, the chosen one filled cyan with navy words, and the place's pin in cyan. The chip's label keeps its
   gold, so "which filter" and "how it is narrowed" read as two things */
.tl-chipext { --tl-cyan: var(--gv-accent, #3AD0EC); }
.tl-chipseg { display: inline-flex; gap: 2px; padding: 2px; border-radius: 999px; border: 1px solid color-mix(in srgb, var(--tl-cyan) 45%, transparent); background: color-mix(in srgb, var(--tl-cyan) 10%, transparent); }
.tl-chipoff, .tl-chipplace { all: unset; box-sizing: border-box; display: inline-flex; align-items: center; height: 24px; border-radius: 999px; font-size: 12.5px; font-weight: 600; white-space: nowrap; cursor: pointer; transition: background .15s, color .15s; }
.tl-chipoff { padding: 0 10px; color: rgba(255, 255, 255, .9); }
.tl-chipoff:hover { color: #fff; background: color-mix(in srgb, var(--tl-cyan) 18%, transparent); }
.tl-chipoff[aria-pressed="true"] { background: var(--tl-cyan); color: #0C1D3A; font-weight: 700; }
.tl-chipplace { gap: 5px; padding: 0 5px 0 7px; font-weight: 700; color: #fff; }
.tl-chipplace:hover { background: color-mix(in srgb, var(--tl-cyan) 18%, transparent); }
.tl-chipplace svg { width: 14px; height: 14px; flex: none; }
.tl-chipplace .tl-pin { color: var(--tl-cyan); }
.tl-chipplace svg:last-child { opacity: .7; }
.tl-chipseg + .tl-chipplace { margin-left: 4px; }
/* (30 Oct) the deals' one "Rayons" button (opens the shelves panel): what is picked, then a chevron */
.tl-chipoff.tl-shelfbtn { gap: 5px; padding-right: 8px; }
.tl-shelfbtn .tl-chev { width: 13px; height: 13px; flex: none; opacity: .8; }
.tl-shelfbtn[aria-expanded="true"] .tl-chev { transform: rotate(180deg); }
.tl-chipoff:focus-visible, .tl-chipplace:focus-visible { outline: 2px solid var(--tl-focus); outline-offset: 1px; }
/* a finger needs more than 26px: on a phone the extras fill most of the 44px chip */
.tl-app.phone .tl-chipoff, .tl-app.phone .tl-chipplace { height: 36px; }
.tl-app.phone .tl-chipoff { padding: 0 11px; }
/* (24 Oct) a ninth chip, "Immobilier" ("Homes"), shows once the line holds a home: the band at 1440 overflowed by 54px
   in French and 12px in English, cutting it to "Imm" under the zoom buttons. Every word kept; only this band's spacing
   tightens (zoom buttons 11px to 7px each side, chips 9px to 8px, gaps 4px to 3px, the gap before the zoom 16px to 12px) */
.tl-app.one.desk .tl-vtools { gap: 12px; }
.tl-app.one.desk .tl-vtools .tl-chips { gap: 3px; }
.tl-app.one.desk .tl-vtools .tl-chip { padding: 0 7px; }   /* (29 Oct) was 8px: with the Deals chip, a pixel a side keeps the band at the headroom it had before it (both languages, 1440) */
/* (30 Oct) 'Cantines' became 'Parascolaire' (Rossi), four letters longer: with the padding and the gaps already at their
   minimum, a tenth of a pixel between letters gives the band its 16px headroom back at 1440 in both languages */
.tl-app.one.desk .tl-vtools .tl-chip { letter-spacing: -.1px; }
.tl-app.one.desk .tl-vtools .tl-seg button { padding: 0 7px; }
/* (24 Oct, after the release gate) GitHub's test browser draws this band about 25px wider than a desktop browser, and the
   band with nine chips had no margin left: the text in it goes to 12.5px and the gap before the zoom to 10px */
.tl-app.one.desk .tl-vtools { gap: 10px; }
.tl-app.one.desk .tl-vtools .tl-chip, .tl-app.one.desk .tl-vtools .tl-seg button { font-size: 12.5px; }

/* (31 Oct) THE CHILD'S SCHOOL FIRST. Rossi: "on the timeline we only show the relevant school menu and collapse the other
   school menus under 'see other school menus' panel". The family's own lunch card wears "Votre école" beside its tag and a
   ring in the lunch colour; the day's other lunch cards fold into one row (its pictures, what it holds, a chevron) that
   opens in place; the cards it lets out rise in one after the other and fade before it folds back. */
.tl-mine { display: inline-flex; align-items: center; gap: 4px; padding: 1px 8px 1px 6px; border-radius: 999px; background: var(--kind-menu); color: #fff; font-size: 11px; font-weight: 700; white-space: nowrap; }
.tl-mine svg { width: 11px; height: 11px; fill: none; stroke: currentColor; stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }
.tl-app.one .tl-vitem.mine, .tl-app.one .tl-vcard.mine:not(.desk) { box-shadow: 0 0 0 2px var(--kind-menu-soft), 0 6px 18px rgba(31, 80, 121, .14); }

.tl-vitem.tl-mg, .tl-vitem.tl-mp { overflow: visible; background: var(--kind-menu-soft); border-color: transparent; box-shadow: none; }
.tl-app.one .tl-vitem.tl-mg, .tl-app.one .tl-vitem.tl-mp { border-top: 0; }
.tl-mg-note { margin: 0; padding: 10px 14px 0; font-size: 12.5px; line-height: 1.4; color: var(--kind-menu-ink); }
.tl-mg-btn, .tl-mp-a { all: unset; box-sizing: border-box; display: flex; align-items: center; gap: 12px; width: 100%; min-height: 56px; padding: 10px 12px 10px 14px; cursor: pointer; color: var(--kind-menu-ink); border-radius: 12px; -webkit-tap-highlight-color: transparent; }
.tl-mg-btn:focus-visible, .tl-mp-a:focus-visible, .tl-mg-chg:focus-visible { outline: 2px solid var(--kind-menu); outline-offset: 2px; }
.tl-mg-pics { display: flex; flex: none; padding-left: 8px; }
.tl-mg-pics img { width: 34px; height: 34px; margin-left: -8px; border-radius: 50%; object-fit: cover; border: 2px solid var(--kind-menu-soft); background: #CFE0EF; box-shadow: 0 1px 3px rgba(31, 80, 121, .2); }
.tl-mg-txt, .tl-mp-txt { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.tl-mg-txt b, .tl-mp-txt b { font-size: 14px; font-weight: 700; line-height: 1.25; color: var(--kind-menu-ink); }
.tl-mg-txt small, .tl-mp-txt small { font-size: 12.5px; line-height: 1.35; color: var(--tl-ink2); }
.tl-mg-chev { display: inline-flex; align-items: center; justify-content: center; flex: none; width: 32px; height: 32px; border-radius: 50%; background: var(--tl-card); box-shadow: 0 1px 2px rgba(31, 80, 121, .18); transition: transform .3s cubic-bezier(.2, .8, .2, 1); }
.tl-mg-chev svg { width: 18px; height: 18px; fill: none; stroke: var(--kind-menu); stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.tl-mg.open .tl-mg-chev { transform: rotate(180deg); }
.tl-mg-btn:hover .tl-mg-chev, .tl-mp-a:hover .tl-mg-chev { box-shadow: 0 2px 8px rgba(31, 80, 121, .28); }
.tl-mg-btn:active, .tl-mp-a:active { background: rgba(45, 111, 163, .08); }
.tl-mg-chg { display: block; margin: -4px 14px 10px; padding: 4px 0; font-size: 12px; font-weight: 500; color: var(--kind-menu-ink); opacity: .8; text-decoration: underline; text-decoration-color: rgba(31, 80, 121, .35); text-underline-offset: 3px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tl-mg-chg:hover { opacity: 1; text-decoration-color: currentColor; }
.tl-mp-ic { display: inline-flex; align-items: center; justify-content: center; flex: none; width: 36px; height: 36px; border-radius: 10px; background: var(--kind-menu); }
.tl-mp-ic svg { width: 20px; height: 20px; fill: none; stroke: #fff; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }

.tl-mg-in { animation: tl-mg-in .42s cubic-bezier(.2, .8, .2, 1) both; animation-delay: calc(var(--mgi, 0) * 40ms); }
@keyframes tl-mg-in { from { opacity: 0; transform: translateY(-10px) scale(.98); } to { opacity: 1; transform: none; } }
.tl-mg-out { transition: opacity .18s ease, transform .18s ease; opacity: 0; transform: translateY(-6px) scale(.98); }
@media (prefers-reduced-motion: reduce) { .tl-mg-in { animation: none; } .tl-mg-chev { transition: none; } }
/* a phone's half-width column (about 150px): the pictures and the chevron on one line, the words under them, the school
   line free to wrap (design check, 31 Oct: side by side, the words stood one to a line) */
.tl-app:not(.desk) .tl-mg-btn, .tl-app:not(.desk) .tl-mp-a { display: grid; grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "pics chev" "txt txt"; align-items: center; gap: 8px; padding: 10px; }
.tl-app:not(.desk) .tl-mg-pics, .tl-app:not(.desk) .tl-mp-ic { grid-area: pics; }
.tl-app:not(.desk) .tl-mg-txt, .tl-app:not(.desk) .tl-mp-txt { grid-area: txt; }
.tl-app:not(.desk) .tl-mg-chev { grid-area: chev; width: 28px; height: 28px; }
.tl-app:not(.desk) .tl-mg-pics img { width: 28px; height: 28px; }
.tl-app:not(.desk) .tl-mg-txt b, .tl-app:not(.desk) .tl-mp-txt b { font-size: 13.5px; }
.tl-app:not(.desk) .tl-mg-txt small, .tl-app:not(.desk) .tl-mp-txt small { font-size: 12px; }
.tl-app:not(.desk) .tl-mg-note { padding: 10px 10px 0; font-size: 12px; }
.tl-app:not(.desk) .tl-mg-chg { margin: -2px 10px 10px; white-space: normal; line-height: 1.35; }
/* the fold's second handle, after the last card it let out */
.tl-vitem.tl-mgt { overflow: visible; background: none; border: 0; box-shadow: none; }
.tl-app.one .tl-vitem.tl-mgt { border-top: 0; }
.tl-mgt-btn { all: unset; box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center; gap: 6px; width: 100%; min-height: 44px; padding: 0 14px; border-radius: 999px; border: 1.5px solid var(--kind-menu); background: var(--tl-card); color: var(--kind-menu-ink); font-size: 13px; font-weight: 700; cursor: pointer; text-align: center; }
.tl-mgt-btn svg { width: 16px; height: 16px; flex: none; fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.tl-mgt-btn:hover { background: var(--kind-menu-soft); }
.tl-mgt-btn:focus-visible { outline: 2px solid var(--kind-menu); outline-offset: 2px; }
.tl-app:not(.desk) .tl-mgt-btn { min-height: 44px; padding: 8px 12px; border-radius: 14px; font-size: 12.5px; line-height: 1.25; }
/* (design review, 31 Oct) the "change school" link is a full tap target on a phone, not a 24px line */
.tl-app:not(.desk) .tl-mg-chg { margin: -6px 0 2px; padding: 10px 10px 12px; }
