/* Slavek Desk — components, part 1: skip link, top bar, tape, install hint,
   hero, overview board, the headlines panel, toasts, footer. Foundation tokens
   and primitives live in app.css.
   Part 2 (watchlist, movers, sectors, news, earnings, sheet) is sections.css. */

/* ---------------------------------------------------------------- shared */

.skip-link {
  position: absolute; left: 12px; top: -60px; z-index: 100;
  padding: 10px 14px; border-radius: var(--r-btn);
  background: var(--bg-3); border: 1px solid var(--line-2); color: var(--text);
  transition: top var(--t-fast);
}
.skip-link:focus { top: 12px; text-decoration: none; }

/* Direction is never colour alone: arrow + word + colour, every time — and the
   arrow is the sign, so the number beside it never carries one (§2). */
.move {
  display: inline-flex; align-items: baseline; gap: 6px;
  font-variant-numeric: tabular-nums lining-nums;
  white-space: nowrap;
}
.move.up { color: var(--up); }
.move.down { color: var(--down); }
.move.flat { color: var(--text-2); }
/* Never below the 12px floor, even on a 13px meta line (§2). */
.move .arrow { font-size: max(12px, .82em); line-height: 1; transform: translateY(-1px); }

.noscript-note {
  max-width: 640px; margin: 40px auto; padding: 16px 18px;
  border: 1px solid var(--line-2); border-radius: var(--r-panel);
  background: var(--bg-2); color: var(--text);
}

/* ------------------------------------------------------------- top bar */

#topbar {
  border-bottom: 1px solid var(--line);
  background: var(--bg-2);
  animation: fade-in 150ms ease-out both;
  /* The page is served with viewport-fit=cover, so on a notched phone the very
     top of the screen belongs to the status bar, not to the clock. */
  padding-top: env(safe-area-inset-top, 0px);
}
.topbar-in {
  display: grid;
  /* minmax(0, auto): the clock is allowed to give ground rather than push the
     status pill off the right-hand edge of the page. */
  grid-template-columns: 1fr minmax(0, auto) 1fr;
  align-items: center;
  gap: 16px;
  min-height: 60px;
  padding-top: 8px; padding-bottom: 8px;
}

.wordmark {
  font-family: var(--font-serif);
  font-weight: 600;
  font-size: 26px;
  line-height: 1;
  color: var(--brand);
  letter-spacing: -0.01em;
  justify-self: start;
}
.wordmark .wordmark-desk {
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: var(--fs-13);
  color: var(--text-2);
  margin-left: 9px;
  letter-spacing: 0;
}

.clock {
  justify-self: center;
  min-width: 0;
  font-size: var(--fs-15);
  color: var(--text-2);
  font-variant-numeric: tabular-nums lining-nums;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.clock .clock-time { color: var(--text); }
/* His own clock beside New York's: quieter, and it is what goes first when the
   header runs out of room (js/ui/topbar.js hides it under 1200px). */
.clock .clock-here { color: var(--text-3); }
.clock .clock-here[hidden] { display: none; }

.topbar-right {
  justify-self: end;
  min-width: 0;
  display: flex; align-items: center; gap: 8px;
}

.pill {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 6px 12px 6px 10px;
  border: 1px solid var(--line-2); border-radius: 999px;
  background: var(--bg-3);
  font-size: var(--fs-15); line-height: 1.2;
  white-space: nowrap;
  /* The pill refused to shrink at any width, so the two 44px icon buttons
     beside it absorbed every pixel of overflow instead: at 320px the settings
     gear ended up 26px wide and entirely off the right of the screen. It gives
     ground here, and the countdown ellipsises rather than pushing the page. */
  min-width: 0;
}
.pill-phase { font-weight: 500; flex: none; }
.pill-count {
  color: var(--text-2);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
.pill-count:not(:empty)::before { content: "· "; color: var(--text-3); }

.dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--text-3);
  flex: none;
}
.pill[data-phase="open"] .dot { background: var(--brand); animation: dot-pulse 2.4s ease-in-out infinite; }
.pill[data-phase="pre"] .dot,
.pill[data-phase="post"] .dot { background: var(--brand); opacity: .55; }

/* Connectivity sits inside the status pill so it is not a stray floating dot. */
.health {
  display: inline-flex; align-items: center;
  margin-left: 2px; padding-left: 9px;
  border-left: 1px solid var(--line-2);
  align-self: stretch;
  flex: none;
}
.health i { width: 8px; height: 8px; border-radius: 50%; background: var(--up); display: block; }
.health[data-health="stale"] i { background: var(--brand); }
.health[data-health="down"] i { background: var(--down); }

.icon-btn {
  width: 40px; height: 40px; border-radius: var(--r-btn);
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--text-2); font-size: 17px; line-height: 1;
  border: 1px solid transparent;
  transition: background var(--t-fast), color var(--t-fast);
}
/* Never the thing that gives way: the gear is the only route to adding a
   stock, holdings, currency, import and reset. */
.topbar-right .icon-btn { flex: none; }
.icon-btn:hover { background: var(--bg-3); color: var(--text); }
@media (max-width: 899px) { .icon-btn { width: 44px; height: 44px; } }

@media (max-width: 899px) {
  /* Longhand only: the `padding` shorthand here wiped .wrap's side gutters and
     ran the wordmark and the clock into the edges of the phone. */
  .topbar-in { grid-template-columns: auto 1fr; row-gap: 4px; min-height: 0; padding-top: 10px; padding-bottom: 10px; }
  .wordmark { font-size: 23px; }
  .clock { grid-column: 2; justify-self: end; font-size: var(--fs-13); }
  .topbar-right { grid-column: 1 / -1; justify-self: stretch; justify-content: flex-start; }
  .topbar-right .icon-btn:first-of-type { margin-left: auto; }
  .pill { padding: 5px 10px 5px 9px; font-size: var(--fs-13); }
}

/* The smallest phones still in service — an iPhone SE or 6s is 320pt — need the
   room the wordmark's subtitle takes, so the clock can carry both times. */
@media (max-width: 359px) {
  .wordmark .wordmark-desk { display: none; }
}

/* ---------------------------------------------------------------- tape */

#tape {
  border-bottom: 1px solid var(--line);
  background: var(--bg);
  animation: fade-in 150ms ease-out 60ms both;
}
.tape-view {
  overflow: hidden;
  position: relative;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 36px, #000 calc(100% - 36px), transparent);
  mask-image: linear-gradient(90deg, transparent, #000 36px, #000 calc(100% - 36px), transparent);
}
.tape-track {
  display: flex;
  width: max-content;
  animation: tape-run var(--tape-dur, 120s) linear infinite;
  will-change: transform;
}
.tape-view:hover .tape-track,
.tape-view:focus-within .tape-track { animation-play-state: paused; }
.tape-half { display: flex; }

.tape-item {
  display: inline-flex; align-items: baseline; gap: 9px;
  /* height:46px clipped the 2px focus ring against .tape-view's overflow:hidden. */
  min-height: 46px; padding: 0 18px;
  border-right: 1px solid var(--line);
  white-space: nowrap;
  color: var(--text);
  transition: background var(--t-fast);
}
.tape-item:hover { background: var(--bg-2); }
/* .tape-view is overflow:hidden, so an outward focus ring is clipped. Safari
   15.0–15.3 drops every :focus-visible rule as invalid, so the offset is
   repeated on plain :focus — which only ever moves a ring the browser was
   already drawing, and so adds nothing anywhere else. */
.tape-item:focus { outline-offset: -2px; }
.tape-item:focus-visible { outline-offset: -2px; }
.tape-item[aria-current="true"] { background: var(--bg-2); box-shadow: inset 0 -2px 0 var(--brand); }
.tape-name { color: var(--text-2); font-size: var(--fs-15); }
.tape-price { font-size: var(--fs-15); font-variant-numeric: tabular-nums lining-nums; }
.tape-item .move { font-size: var(--fs-13); }

.tape-empty { padding: 14px 0; font-size: var(--fs-15); }

@media (prefers-reduced-motion: reduce) {
  /* No marquee: let him scroll the tape by hand instead. */
  .tape-view { overflow-x: auto; scrollbar-width: none; }
  .tape-view::-webkit-scrollbar { display: none; }
}

/* ------------------------------------------------- add-to-home-screen hint */

/* Phones only, from the third visit, dismissed for good on "Not now".
   One row under the tape with a brass edge — no icon, no card, no badge. */
.install-hint {
  background: var(--bg-2);
  border-left: 3px solid var(--brand);
  border-bottom: 1px solid var(--line);
}
.ih-in {
  display: flex; align-items: flex-start; gap: 12px;
  padding-top: 10px; padding-bottom: 10px;
}
.ih-text { flex: 1; min-width: 0; font-size: var(--fs-15); line-height: 1.45; }
.ih-how { display: block; color: var(--text-2); font-size: var(--fs-13); }
.ih-no {
  flex: none; min-height: 34px; padding: 0 10px;
  border-radius: var(--r-btn);
  color: var(--text-2); font-size: var(--fs-13); white-space: nowrap;
  transition: background var(--t-fast), color var(--t-fast);
}
.ih-no:hover { background: var(--bg-3); color: var(--text); }
@media (max-width: 899px) { .ih-no { min-height: 44px; } }

/* ---------------------------------------------------------------- hero */

#hero.hero { display: flex; flex-direction: column; gap: 16px; }

.hero-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
.hero-name {
  font-family: var(--font-serif); font-weight: 500;
  font-size: var(--fs-28); line-height: 1.2; letter-spacing: -0.01em;
}
.hero-meta {
  margin-top: 4px;
  font-size: var(--fs-13); color: var(--text-2);
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
.hero-ticker { color: var(--text); font-weight: 500; font-size: var(--fs-15); }
.hero-exch::before { content: ""; }

/* One plain sentence about an instrument nobody is born knowing — the VIX, the
   dollar index, the ten-year. The quote board carries the same words in a hover
   title; this is how a thumb gets to them. */
.hero-note {
  margin-top: 4px;
  font-size: var(--fs-13);
  line-height: 1.45;
  color: var(--text-2);
  max-width: 46ch;
}
.hero-note[hidden] { display: none; }

.star {
  display: inline-flex; align-items: center; gap: 7px;
  min-height: 36px; padding: 0 12px;
  border: 1px solid var(--line-2); border-radius: var(--r-btn);
  color: var(--text-2); font-size: var(--fs-13); white-space: nowrap;
  transition: color var(--t-fast), border-color var(--t-fast), background var(--t-fast);
}
.star:hover { color: var(--text); background: var(--bg-3); }
/* Two declarations on purpose: color-mix needs Safari 16.2 / Chromium 111, and
   an unparseable value takes the whole declaration with it — which left the
   pressed star with the same border as the unpressed one. The plain brass
   below is what those browsers keep. */
.star[aria-pressed="true"] {
  color: var(--brand-ink);
  border-color: var(--brand);
  border-color: color-mix(in srgb, var(--brand) 45%, transparent);
}
.star .star-glyph { font-size: 15px; line-height: 1; }
@media (max-width: 899px) { .star { min-height: 44px; } }

.hero-price-row { display: flex; align-items: baseline; gap: 16px; flex-wrap: wrap; }
.hero-price {
  font-size: var(--fs-56); font-weight: 500; line-height: 1.05;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums lining-nums;
  border-radius: 6px;
}
.hero-move { font-size: var(--fs-22); }
.hero-move .arrow { font-size: .74em; }

.hero-ext { font-size: var(--fs-15); color: var(--text-2); margin-top: -6px; }
.hero-ext .move { font-size: var(--fs-15); }
.hero-ext-time { color: var(--text-2); }

/* The one memorable moment: the chart wipes in from the left, once. */
.hero-chart-wrap { position: relative; }
.hero-chart-wrap.reveal-chart { animation: wipe-in 700ms cubic-bezier(.22, .61, .36, 1) both; }
#hero-chart {
  height: 360px; min-height: 360px; width: 100%;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.hero-chart-note {
  margin-top: 8px;
  font-size: var(--fs-13); color: var(--text-2);
}
.hero-chart-empty {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  color: var(--text-2); font-size: var(--fs-15); text-align: center; padding: 0 20px;
}

.ranges { display: flex; flex-wrap: wrap; gap: 2px; }
.range-btn {
  position: relative;
  min-height: 36px; padding: 0 11px;
  border-radius: var(--r-btn);
  color: var(--text-2); font-size: var(--fs-15); font-weight: 500;
  transition: color var(--t-fast), background var(--t-fast);
}
.range-btn:hover:not([aria-disabled="true"]) { color: var(--text); background: var(--bg-3); }
.range-btn[aria-pressed="true"] { color: var(--text); }
/* A fund prices once a day, so 1D and 5D are switched off. Say so with the
   cursor too: lighting up under the pointer would promise a click that the
   button will not answer. */
.range-btn[aria-disabled="true"] { color: var(--text-3); cursor: default; }
.range-btn[aria-pressed="true"]::after {
  content: ""; position: absolute; left: 11px; right: 11px; bottom: 3px;
  height: 2px; border-radius: 1px; background: var(--brand);
}

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(104px, 1fr));
  gap: 14px 20px;
  border-top: 1px solid var(--line);
  padding-top: 16px;
}
/* Nine numbers plus the wide 52-week cell = ten cells: two even rows of five. */
@media (min-width: 620px) {
  .stats { grid-template-columns: repeat(5, minmax(0, 1fr)); }
  .stat.wide { grid-column: 1 / span 2; }
}
.stat { min-width: 0; }
.stat-k { font-size: var(--fs-13); color: var(--text-2); }
.stat-v {
  font-size: var(--fs-15); color: var(--text); margin-top: 2px;
  font-variant-numeric: tabular-nums lining-nums;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.stat-sub { font-size: var(--fs-13); color: var(--text-2); margin-top: 1px; }
.stat.wide { grid-column: span 2; }
.stat .bar { margin-top: 9px; }
.stat .bar > i { background: var(--brand); width: 8px; height: 8px; top: -2px; margin-left: -4px; }

@media (min-width: 1100px) {
  /* The hero spans the quote board and the headlines beside it, so it is
     usually taller than its own content. The chart takes the slack: it is the
     one thing on this page that is better big. */
  /* flex-basis 0 and an absolutely positioned chart: the library writes a pixel
     height onto its own element, and if that height counted as content the hero
     would grow a little taller on every redraw. */
  .hero-chart-wrap { flex: 1 1 0%; min-height: 360px; }
  #hero-chart { position: absolute; inset: 0; height: auto; min-height: 0; }
}

@media (max-width: 759px) {
  .hero-name { font-size: var(--fs-22); }
  .hero-price { font-size: 40px; }
  .hero-move { font-size: var(--fs-18); }
  #hero-chart { height: 240px; min-height: 240px; }
  /* Nine 31px buttons shoulder to shoulder was a lottery for the reader this
     page exists for. Two even rows of real targets, on a grid so none of them
     ends up narrower than its neighbour (§2 legibility, 44px touch floor). */
  .ranges { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 4px; }
  .range-btn { padding: 0 6px; min-height: 44px; }
  .range-btn[aria-pressed="true"]::after { left: 8px; right: 8px; }
  .stats { grid-template-columns: repeat(auto-fit, minmax(104px, 1fr)); gap: 12px 14px; }
}

/* ------------------------------------------------------- overview board */

#overview { display: flex; flex-direction: column; }
#overview .panel-head { margin-bottom: 6px; }
/* The tiles are buttons; nothing on a phone hints at that, so say it. */
.ov-sub { margin: 0 0 10px; }

/* Not twelve floating cards: one quote board ruled by hairlines. */
.board {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--r-panel);
  overflow: hidden;
}
.tile {
  display: flex; flex-direction: column; gap: 2px;
  background: var(--bg-2);
  padding: 12px 13px 10px;
  min-height: 106px;
  text-align: left;
  position: relative;
  transition: background var(--t-fast);
  animation: tile-in 220ms ease-out both;
  animation-delay: calc(var(--i, 0) * 30ms);
}
.tile:hover { background: var(--bg-3); }
/* Same clipping, same Safari 15.0–15.3 fallback as .tape-item above: .board is
   overflow:hidden, so the ring has to sit inside the tile. */
.tile:focus { outline-offset: -2px; border-radius: 0; }
.tile:focus-visible { outline-offset: -2px; border-radius: 0; }
.tile[aria-current="true"]::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
  background: var(--brand);
}
.tile-label {
  font-size: var(--fs-13); color: var(--text-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tile-price {
  font-size: var(--fs-22); line-height: 1.2; color: var(--text);
  font-variant-numeric: tabular-nums lining-nums;
  border-radius: 4px;
}
.tile .move { font-size: var(--fs-13); }
.tile-spark { margin-top: auto; width: 100%; height: 26px; opacity: .9; }

@media (min-width: 1100px) {
  /* The board and the headlines panel below it together set the height of this
     column; the hero spans both. The board still stretches into whatever room
     is left rather than leaving a gap under the last row of tiles. */
  #overview { align-self: stretch; }
  #overview .board { flex: 1; grid-auto-rows: minmax(132px, 1fr); }
}
@media (min-width: 760px) and (max-width: 1099px) {
  .board { grid-template-columns: repeat(6, minmax(0, 1fr)); }
}
@media (max-width: 759px) {
  .board { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .tile { min-height: 96px; }
  .tile-price { font-size: var(--fs-18); }
}

/* ------------------------------------------- headlines on the selected stock */

/* Lifted out of the hero: down here it fills the column beside it instead of
   making the left side of the page 400px taller than the right. */
#symbol-news { display: flex; flex-direction: column; }
#symbol-news .panel-head { margin-bottom: 10px; }
.sn-list:not(:empty) { flex: 1; }
/* An index has no company news. Centring the line makes that read as an answer
   rather than as a panel that failed to fill. */
.sn-empty { flex: 1; display: flex; align-items: center; }
.sn-empty[hidden] { display: none; }

.sn-item { padding: 11px 0; border-bottom: 1px solid var(--line); }
.sn-item:first-child { padding-top: 0; }
.sn-item:last-child { border-bottom: 0; }
.sn-title {
  font-size: var(--fs-16);
  font-weight: 500;
  line-height: 1.4;
  display: inline-block;
}
.sn-title:hover { color: var(--brand); }
.sn-meta { display: flex; align-items: center; gap: 8px; margin-top: 3px; }
.sn-meta > span { display: inline-flex; align-items: center; }
.sn-kind { font-size: var(--fs-12); padding: 1px 7px; }
.sn-src { margin-top: 12px; }

/* -------------------------------------------------------------- toasts */

#toasts {
  position: fixed; left: var(--gutter); bottom: var(--gutter);
  z-index: 80;
  display: flex; flex-direction: column-reverse; gap: 8px;
  width: min(380px, calc(100vw - var(--gutter) * 2));
  pointer-events: none;
}
.toast {
  pointer-events: auto;
  display: flex; align-items: flex-start; gap: 10px;
  padding: 11px 12px;
  background: var(--bg-3);
  border: 1px solid var(--line-2);
  border-left: 3px solid var(--brand);
  border-radius: var(--r-btn);
  box-shadow: 0 8px 28px rgba(3, 8, 18, .34);
  font-size: var(--fs-15); line-height: 1.4;
  animation: toast-in 180ms ease-out both;
}
.toast[data-kind="done"] { border-left-color: var(--up); }
.toast[data-kind="problem"] { border-left-color: var(--down); }
.toast[data-leaving="true"] { animation: toast-out 160ms ease-in both; }
.toast-text { flex: 1; min-width: 0; }
.toast-close {
  flex: none; width: 26px; height: 26px; border-radius: 6px;
  color: var(--text-2); font-size: 16px; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
}
.toast-close:hover { background: var(--bg-2); color: var(--text); }

@media (max-width: 759px) {
  #toasts {
    left: var(--gutter); right: var(--gutter); width: auto;
    bottom: calc(12px + env(safe-area-inset-bottom, 0px));
  }
}

/* -------------------------------------------------------------- footer */

#foot {
  border-top: 1px solid var(--line);
  /* viewport-fit=cover runs the page under the home indicator; the last line
     of the page should clear it. */
  padding: 20px 0 calc(32px + env(safe-area-inset-bottom, 0px));
  font-size: var(--fs-13);
  color: var(--text-2);
  display: flex; flex-direction: column; gap: 4px;
}
#foot a { text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--line-2); }
#foot a:hover { color: var(--text); }
#foot-refresh:not(:empty)::before { content: " · "; }

/* ------------------------------------------------------------- motion */

@keyframes fade-in {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: none; }
}
@keyframes tile-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}
@keyframes wipe-in {
  from { clip-path: inset(0 100% 0 0); }
  to { clip-path: inset(0 0 0 0); }
}
@keyframes tape-run {
  from { transform: translate3d(0, 0, 0); }
  to { transform: translate3d(-50%, 0, 0); }
}
@keyframes dot-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: .38; }
}
@keyframes toast-in {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}
@keyframes toast-out {
  from { opacity: 1; }
  to { opacity: 0; transform: translateY(6px); }
}
