/* ==========================================================================
   InvestorPA — theme layer
   --------------------------------------------------------------------------
   Built on Bootstrap 5.3's CSS-variable API.

   Rules of the road:
     1. Our tokens are `--ipa-*`. Bootstrap's are `--bs-*`. We define ours,
        then MAP them onto Bootstrap's so framework components pick them up.
     2. Never restyle a Bootstrap component with a blanket selector
        (`.btn { background: ... }`). Use the component's own local variables
        (`--bs-btn-bg`) so variants keep working.
     3. Every colour token is defined for light AND dark. No colour may have
        its only definition inside one theme.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Raw scales — theme-independent values
   -------------------------------------------------------------------------- */

:root {
  /* Brand purple, built around the logo colour #5c2c91 (= --ipa-purple-600) */
  --ipa-purple-50:  #f6f3fb;
  --ipa-purple-100: #ece4f6;
  --ipa-purple-200: #d9c9ec;
  --ipa-purple-300: #bda4dd;
  --ipa-purple-400: #9c78ca;
  --ipa-purple-500: #7d51b2;
  --ipa-purple-600: #5c2c91;
  --ipa-purple-700: #4a2273;
  --ipa-purple-800: #391a58;
  --ipa-purple-900: #2a123f;
  --ipa-purple-950: #1b0b29;

  /* Neutrals — true grey, the same family as the charcoal chrome (#212529).
     These used to carry a trace of the brand violet, which put every band,
     border and stripe in the same hue as the logo, links and buttons, so the
     page read as one undifferentiated purple wash. Structure is now neutral
     and purple is reserved for brand and things you can click. */
  --ipa-gray-0:   #ffffff;
  --ipa-gray-25:  #fcfcfd;
  --ipa-gray-50:  #f8f9fa;
  --ipa-gray-100: #f1f3f5;
  --ipa-gray-200: #e9ecef;
  --ipa-gray-300: #dee2e6;
  --ipa-gray-400: #ced4da;
  --ipa-gray-500: #adb5bd;
  --ipa-gray-600: #545b62;
  --ipa-gray-700: #495057;
  --ipa-gray-800: #343a40;
  --ipa-gray-900: #212529;
  --ipa-gray-950: #1a1d21;
  --ipa-gray-1000:#121416;

  /* Semantic hues — one light-theme and one dark-theme value each */
  --ipa-red-500:    #c0392b;
  --ipa-red-600:    #a02d20;
  --ipa-red-300:    #f0897a;
  --ipa-amber-500:  #b06f12;
  --ipa-amber-300:  #e5aa5c;
  --ipa-green-500:  #2b7a4b;
  --ipa-green-300:  #63c08b;
  --ipa-blue-500:   #2f63a0;
  --ipa-blue-300:   #82b1e4;

  /* Type scale */
  --ipa-text-xs:   0.75rem;
  --ipa-text-sm:   0.8125rem;
  --ipa-text-base: 0.9375rem;
  --ipa-text-md:   1rem;
  --ipa-text-lg:   1.125rem;
  --ipa-text-xl:   1.375rem;
  --ipa-text-2xl:  1.75rem;
  --ipa-text-3xl:  2.25rem;

  /* Spacing scale */
  --ipa-space-1: 0.25rem;
  --ipa-space-2: 0.5rem;
  --ipa-space-3: 0.75rem;
  --ipa-space-4: 1rem;
  --ipa-space-5: 1.5rem;
  --ipa-space-6: 2rem;
  --ipa-space-7: 3rem;
  --ipa-space-8: 4rem;

  /* Radii */
  --ipa-radius-sm:   0.25rem;
  --ipa-radius-md:   0.375rem;
  --ipa-radius-lg:   0.5rem;
  --ipa-radius-xl:   0.75rem;
  --ipa-radius-pill: 999px;

  /* Elevation */
  --ipa-shadow-sm: 0 1px 2px rgba(18, 20, 22, 0.08);
  --ipa-shadow-md: 0 1px 3px rgba(18, 20, 22, 0.10), 0 4px 12px rgba(18, 20, 22, 0.08);

  /* Chrome — nav bar and footer.
     Deliberately theme-INVARIANT: the same dark frame in light and dark mode.
     Dark mode's chrome was already dark, so pinning it here means one nav
     treatment to maintain instead of two, and the brand reads identically in
     both. A dark frame around a light work surface is the usual shape for a
     data tool, and it is what gives the white page an edge to sit inside.
     Because this furniture no longer follows the theme, everything drawn on it
     needs an explicit on-chrome colour — including the hamburger icon, whose
     stroke is baked into a data-URI further down. */
  /* Neutral charcoal, not brand purple. A purple frame put the logo, buttons,
     links and chrome all in one hue, so nothing stood apart; a neutral frame
     lets the purple read as the accent it is. */
  --ipa-chrome-bg:        #212529;
  --ipa-chrome-text:      #ced4da;   /* idle nav links   — 10.3:1 */
  --ipa-chrome-text-bold: #ffffff;   /* brand + active   — 15.4:1 */
  --ipa-chrome-accent:    var(--ipa-purple-300);      /* underline — 7.0:1 */
  --ipa-chrome-hover:     rgba(255, 255, 255, 0.08);
  --ipa-chrome-border:    rgba(255, 255, 255, 0.25);
}

/* --------------------------------------------------------------------------
   2. Role tokens — light theme
   -------------------------------------------------------------------------- */

:root {
  --ipa-surface:         var(--ipa-gray-0);
  --ipa-surface-sunken:  var(--ipa-gray-50);
  --ipa-surface-raised:  var(--ipa-gray-0);
  --ipa-surface-hover:   var(--ipa-gray-50);
  /* Row hover. 1.27:1 on white, and still 1.11:1 against a stripe band. */
  --ipa-surface-inset:   #e1e5e9;
  /* Group bands inside content. The nav and footer no longer use this — they
     are on --ipa-chrome-bg. */
  --ipa-surface-chrome:  var(--ipa-gray-50);
  /* Zebra band. Must stay below --ipa-surface-inset, which is what rows hover
     to, so hover still reads on a striped row. */
  --ipa-surface-stripe:  #eef0f3;   /* 1.14:1 on white — was 1.07, too faint to track */
  /* Column-heading and date-group bands. The darkest band in the table, so a
     heading never reads as just another stripe. */
  --ipa-surface-header:  #dee2e6;

  --ipa-border:          #dee2e6;   /* dividers, 1.30:1 on white */
  --ipa-border-strong:   #9aa2aa;   /* panel outlines, 2.59:1 — a real edge */
  /* Boundaries that identify a control (inputs, outline buttons). WCAG 1.4.11
     asks 3:1 for these. */
  --ipa-border-control:  #868e96;   /* 3.32:1 */

  --ipa-text:            var(--ipa-gray-900);
  --ipa-text-secondary:  var(--ipa-gray-700);
  --ipa-text-muted:      var(--ipa-gray-600);
  --ipa-text-faint:      #5c636a;   /* 4.68:1 on the header band, the darkest surface */
  --ipa-heading:         var(--ipa-gray-950);

  --ipa-accent:          var(--ipa-purple-600);
  --ipa-accent-hover:    var(--ipa-purple-700);
  --ipa-accent-solid:    var(--ipa-purple-600);
  --ipa-accent-solid-hover: var(--ipa-purple-700);
  --ipa-accent-subtle:   var(--ipa-purple-50);
  --ipa-accent-border:   var(--ipa-purple-200);
  --ipa-on-accent:       #ffffff;
  --ipa-accent-rgb:      92, 44, 145;

  --ipa-critical:        var(--ipa-red-500);
  --ipa-critical-subtle: #fceceb;
  --ipa-positive:        var(--ipa-green-500);
  --ipa-positive-subtle: #e8f4ec;
  --ipa-caution:         #9e6410;   /* --ipa-amber-500 was 4.10:1 — just under AA */
  --ipa-caution-subtle:  #fbf1e1;
  --ipa-info:            var(--ipa-blue-500);
  --ipa-info-subtle:     #e9f0f8;

  color-scheme: light;
}

/* --------------------------------------------------------------------------
   3. Role tokens — dark theme
   Loaded after Bootstrap, so equal-specificity rules win.
   -------------------------------------------------------------------------- */

[data-bs-theme="dark"] {
  --ipa-surface:         var(--ipa-gray-950);
  --ipa-surface-sunken:  var(--ipa-gray-1000);
  --ipa-surface-raised:  var(--ipa-gray-900);
  --ipa-surface-hover:   #23272b;
  /* Must stay visibly above the card surface — an earlier value measured
     1.01:1, a hover state that could not be seen at all. */
  --ipa-surface-inset:   #343a40;
  --ipa-surface-chrome:  var(--ipa-gray-1000);
  --ipa-surface-stripe:  #23272c;
  --ipa-surface-header:  #2b3035;

  --ipa-border:          #3a4047;
  --ipa-border-strong:   #4d555d;
  --ipa-border-control:  #6c757d;

  --ipa-text:            #e9ecef;
  --ipa-text-secondary:  #c4c9ce;
  --ipa-text-muted:      #9ea5ab;
  --ipa-text-faint:      #a1a8ae;   /* AA on every band, hover included */
  --ipa-heading:         #f8f9fa;

  --ipa-accent:          var(--ipa-purple-300);
  --ipa-accent-hover:    var(--ipa-purple-200);
  --ipa-accent-solid:    var(--ipa-purple-500);
  --ipa-accent-solid-hover: var(--ipa-purple-400);
  --ipa-accent-subtle:   rgba(157, 120, 202, 0.14);
  --ipa-accent-border:   rgba(157, 120, 202, 0.35);
  --ipa-on-accent:       #ffffff;
  --ipa-accent-rgb:      189, 164, 221;

  --ipa-critical:        var(--ipa-red-300);
  --ipa-critical-subtle: rgba(240, 137, 122, 0.13);
  --ipa-positive:        var(--ipa-green-300);
  --ipa-positive-subtle: rgba(99, 192, 139, 0.13);
  --ipa-caution:         var(--ipa-amber-300);
  --ipa-caution-subtle:  rgba(229, 170, 92, 0.13);
  --ipa-info:            var(--ipa-blue-300);
  --ipa-info-subtle:     rgba(130, 177, 228, 0.13);

  --ipa-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
  --ipa-shadow-md: 0 1px 3px rgba(0, 0, 0, 0.45), 0 4px 12px rgba(0, 0, 0, 0.35);

  color-scheme: dark;
}

/* --------------------------------------------------------------------------
   4. Map our tokens onto Bootstrap
   -------------------------------------------------------------------------- */

:root,
[data-bs-theme="dark"] {
  --bs-body-bg:          var(--ipa-surface);
  --bs-body-color:       var(--ipa-text);
  --bs-emphasis-color:   var(--ipa-heading);
  --bs-secondary-color:  var(--ipa-text-muted);
  --bs-tertiary-color:   var(--ipa-text-faint);
  --bs-secondary-bg:     var(--ipa-surface-inset);
  --bs-tertiary-bg:      var(--ipa-surface-sunken);
  --bs-border-color:     var(--ipa-border);

  --bs-primary:          var(--ipa-accent);
  --bs-primary-rgb:      var(--ipa-accent-rgb);

  --bs-link-color:       var(--ipa-accent);
  --bs-link-color-rgb:   var(--ipa-accent-rgb);
  --bs-link-hover-color: var(--ipa-accent-hover);

  --bs-heading-color:    var(--ipa-heading);

  /* Inter, loaded from Google Fonts in base.html. The system stack is the
     fallback while it loads (display=swap) and if the CDN is unreachable. */
  --bs-body-font-family: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --bs-body-font-size:   var(--ipa-text-base);
  --bs-body-line-height: 1.6;

  --bs-border-radius:    var(--ipa-radius-md);
  --bs-border-radius-sm: var(--ipa-radius-sm);
  --bs-border-radius-lg: var(--ipa-radius-lg);

  --bs-focus-ring-color: rgba(var(--ipa-accent-rgb), 0.25);
  --bs-focus-ring-width: 3px;
}

/* --------------------------------------------------------------------------
   5. Base typography
   -------------------------------------------------------------------------- */

/* Reserve the scrollbar's space on every page. Without it, short pages are a
   scrollbar wider than long ones and the whole layout, navbar included,
   jumps sideways when you move between them. */
html { scrollbar-gutter: stable; }

body {
  background-color: var(--ipa-surface);
  color: var(--ipa-text);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
  color: var(--ipa-heading);
  font-weight: 600;
  letter-spacing: -0.014em;
  text-wrap: balance;
}

/* Page titles carry the brand colour, as they did before the redesign. With
   the frame and structure neutral, it reads as an accent rather than noise. */
h1, .h1 { font-size: var(--ipa-text-2xl); color: var(--ipa-accent); }
h2, .h2 { font-size: var(--ipa-text-xl); }
h3, .h3 { font-size: var(--ipa-text-lg); }
h4, .h4 { font-size: var(--ipa-text-md); }
h5, .h5 { font-size: var(--ipa-text-base); }
h6, .h6 { font-size: var(--ipa-text-sm); }

.display-4, .display-5 {
  color: var(--ipa-heading);
  font-weight: 700;
  letter-spacing: -0.026em;
}

/* Numerals line up in columns — dates, times, scores, prices. Scoped to the
   numeric cells, not whole tables: Inter's tnum also gives hyphens and other
   punctuation a fixed width, which opens gaps inside headlines
   ("Non - Executive"). */
.ipa-tnum, time, .ipa-feed-time, .ipa-feed-score, .ipa-item-meta, .ipa-pagination-status {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum";
}

a {
  text-underline-offset: 0.16em;
  text-decoration-thickness: from-font;
}

/* --------------------------------------------------------------------------
   6. Bootstrap component overrides — via component variables only
   -------------------------------------------------------------------------- */

/* Buttons. The previous stylesheet styled every `.btn` white-and-purple,
   which silently killed every Bootstrap variant. Only `primary` is retuned. */
.btn {
  --bs-btn-font-weight: 500;
  --bs-btn-padding-x: 0.875rem;
  --bs-btn-padding-y: 0.4375rem;
}

.btn-primary {
  --bs-btn-bg: var(--ipa-accent-solid);
  --bs-btn-border-color: var(--ipa-accent-solid);
  --bs-btn-color: var(--ipa-on-accent);
  --bs-btn-hover-bg: var(--ipa-accent-solid-hover);
  --bs-btn-hover-border-color: var(--ipa-accent-solid-hover);
  --bs-btn-hover-color: var(--ipa-on-accent);
  --bs-btn-active-bg: var(--ipa-accent-solid-hover);
  --bs-btn-active-border-color: var(--ipa-accent-solid-hover);
  --bs-btn-active-color: var(--ipa-on-accent);
  --bs-btn-disabled-bg: var(--ipa-accent-solid);
  --bs-btn-disabled-border-color: var(--ipa-accent-solid);
  --bs-btn-disabled-color: var(--ipa-on-accent);
}

.btn-outline-primary {
  --bs-btn-color: var(--ipa-accent);
  --bs-btn-border-color: var(--ipa-accent);
  --bs-btn-hover-bg: var(--ipa-accent-solid);
  --bs-btn-hover-border-color: var(--ipa-accent-solid);
  --bs-btn-hover-color: var(--ipa-on-accent);
  --bs-btn-active-bg: var(--ipa-accent-solid);
  --bs-btn-active-border-color: var(--ipa-accent-solid);
  --bs-btn-active-color: var(--ipa-on-accent);
  --bs-btn-disabled-color: var(--ipa-text-faint);
  --bs-btn-disabled-border-color: var(--ipa-border);
}

.btn-outline-secondary {
  --bs-btn-color: var(--ipa-text-secondary);
  --bs-btn-border-color: var(--ipa-border-control);
  --bs-btn-hover-bg: var(--ipa-surface-inset);
  --bs-btn-hover-border-color: var(--ipa-border-control);
  --bs-btn-hover-color: var(--ipa-text);
  --bs-btn-active-bg: var(--ipa-surface-inset);
  --bs-btn-active-border-color: var(--ipa-border-control);
  --bs-btn-active-color: var(--ipa-text);
}

/* Segmented filter control (All / Following) */
.ipa-segmented {
  display: inline-flex;
  padding: 3px;
  gap: 2px;
  background-color: var(--ipa-surface-inset);
  border-radius: var(--ipa-radius-lg);
}

.ipa-segmented > a {
  padding: 0.3125rem 0.875rem;
  border-radius: var(--ipa-radius-md);
  font-size: var(--ipa-text-sm);
  font-weight: 500;
  color: var(--ipa-text-muted);
  text-decoration: none;
  transition: background-color 0.12s ease, color 0.12s ease;
}

.ipa-segmented > a:hover { color: var(--ipa-text); }

.ipa-segmented > a.active {
  background-color: var(--ipa-surface);
  color: var(--ipa-accent);
  box-shadow: var(--ipa-shadow-sm);
}

/* Forms */
.form-control, .form-select {
  /* An input's border is what identifies it as an input, so it takes the 3:1
     control token rather than the softer divider one. */
  --bs-border-color: var(--ipa-border-control);
  background-color: var(--ipa-surface);
  color: var(--ipa-text);
  padding: 0.4375rem 0.75rem;
}

.form-control:focus, .form-select:focus {
  border-color: var(--ipa-accent);
  box-shadow: 0 0 0 3px rgba(var(--ipa-accent-rgb), 0.18);
}

.form-control::placeholder { color: var(--ipa-text-faint); }
.form-label { font-weight: 500; font-size: var(--ipa-text-sm); color: var(--ipa-text-secondary); }
.form-text  { color: var(--ipa-text-muted); font-size: var(--ipa-text-xs); }

/* Cards */
.card {
  --bs-card-bg: var(--ipa-surface-raised);
  --bs-card-border-color: var(--ipa-border);
  --bs-card-border-radius: var(--ipa-radius-xl);
  --bs-card-cap-bg: var(--ipa-surface-chrome);
  /* Must be the outer radius minus the border width. Setting an unrelated,
     smaller value leaves the card's own background visible in the top corners
     behind the header — the corners look chipped. */
  --bs-card-inner-border-radius: calc(var(--ipa-radius-xl) - 1px);
}

.card-header {
  border-bottom-color: var(--ipa-border);
  color: var(--ipa-text-secondary);
  font-size: var(--ipa-text-sm);
}

/* Bootstrap's bare colour utilities don't follow our theme — `.bg-light` stays
   near-white in dark mode. Remap the few the templates use. */
.bg-light {
  background-color: var(--ipa-surface-inset) !important;
  color: var(--ipa-text-secondary);
}

.border-secondary { border-color: var(--ipa-border) !important; }

/* Pagination */
.pagination {
  --bs-pagination-color: var(--ipa-text-secondary);
  --bs-pagination-bg: transparent;
  --bs-pagination-border-color: transparent;
  --bs-pagination-hover-color: var(--ipa-accent);
  --bs-pagination-hover-bg: var(--ipa-surface-inset);
  --bs-pagination-hover-border-color: transparent;
  --bs-pagination-focus-bg: var(--ipa-surface-inset);
  --bs-pagination-focus-color: var(--ipa-accent);
  --bs-pagination-active-bg: var(--ipa-accent-solid);
  --bs-pagination-active-border-color: var(--ipa-accent-solid);
  --bs-pagination-active-color: var(--ipa-on-accent);
  --bs-pagination-disabled-color: var(--ipa-text-faint);
  --bs-pagination-disabled-bg: transparent;
  --bs-pagination-disabled-border-color: transparent;
  --bs-pagination-border-radius: var(--ipa-radius-md);
  --bs-pagination-padding-x: 0.625rem;
  --bs-pagination-padding-y: 0.3125rem;
  --bs-pagination-font-size: var(--ipa-text-sm);
  gap: 2px;
}

/* Dropdowns */
.dropdown-menu {
  --bs-dropdown-bg: var(--ipa-surface-raised);
  --bs-dropdown-border-color: var(--ipa-border);
  --bs-dropdown-border-radius: var(--ipa-radius-lg);
  --bs-dropdown-link-color: var(--ipa-text-secondary);
  --bs-dropdown-link-hover-bg: var(--ipa-surface-hover);
  --bs-dropdown-link-hover-color: var(--ipa-text);
  --bs-dropdown-link-active-bg: var(--ipa-accent-subtle);
  --bs-dropdown-link-active-color: var(--ipa-accent);
  --bs-dropdown-font-size: var(--ipa-text-sm);
  --bs-dropdown-padding-y: 0.3125rem;
  --bs-dropdown-item-padding-y: 0.4375rem;
  box-shadow: var(--ipa-shadow-md);
}

/* Alerts.
   Bootstrap's defaults are loud, chunky and use their own palette, so a success
   message arrived as a slab of unrelated green. Every level is retuned here:
   compact, on a tinted ground from our own tokens, with a coloured edge and an
   icon carrying the meaning. All six levels are covered — Django emits
   `success`, `info`, `warning`, `danger` and `debug`. */
.alert {
  --bs-alert-padding-x: 0.875rem;
  --bs-alert-padding-y: 0.625rem;
  --bs-alert-margin-bottom: var(--ipa-space-4);
  --bs-alert-border-radius: var(--ipa-radius-lg);
  --bs-alert-color: var(--ipa-text-secondary);
  --bs-alert-bg: var(--ipa-surface-sunken);
  --bs-alert-border-color: var(--ipa-border);
  border-left-width: 3px;
  font-size: var(--ipa-text-sm);
  line-height: 1.55;
}

.alert > :last-child { margin-bottom: 0; }
.alert p:last-child, .alert ul:last-child { margin-bottom: 0; }

/* Icon-first layout. Deliberately a separate class rather than applying flex to
   every `.alert`: some alerts on the site hold a multi-child block (a grid plus
   a footnote), and turning those into flex rows laid their children out
   side-by-side and overflowed the page on mobile. */
.ipa-alert {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
}

.ipa-alert > .bi { flex: 0 0 auto; margin-top: 0.0625rem; font-size: 0.9375rem; }

.alert-success {
  --bs-alert-bg: var(--ipa-positive-subtle);
  --bs-alert-border-color: var(--ipa-positive);
  --bs-alert-color: var(--ipa-text-secondary);
}
.alert-success > .bi { color: var(--ipa-positive); }

.alert-danger,
.alert-error {
  --bs-alert-bg: var(--ipa-critical-subtle);
  --bs-alert-border-color: var(--ipa-critical);
  --bs-alert-color: var(--ipa-text-secondary);
}
.alert-danger > .bi, .alert-error > .bi { color: var(--ipa-critical); }

.alert-warning {
  --bs-alert-bg: var(--ipa-caution-subtle);
  --bs-alert-border-color: var(--ipa-caution);
  --bs-alert-color: var(--ipa-text-secondary);
}
.alert-warning > .bi { color: var(--ipa-caution); }

.alert-info {
  --bs-alert-bg: var(--ipa-info-subtle);
  --bs-alert-border-color: var(--ipa-info);
  --bs-alert-color: var(--ipa-text-secondary);
}
.alert-info > .bi { color: var(--ipa-info); }

.alert-debug,
.alert-secondary,
.alert-light {
  --bs-alert-bg: var(--ipa-surface-sunken);
  --bs-alert-color: var(--ipa-text-secondary);
  --bs-alert-border-color: var(--ipa-border-strong);
}
.alert-debug > .bi, .alert-secondary > .bi, .alert-light > .bi { color: var(--ipa-text-muted); }

/* `alert-error` is not a Bootstrap class, so it carries no base styling.
   Django emits it unless MESSAGE_TAGS remaps ERROR (which settings.py now
   does) — this is belt-and-braces for any template hard-coding it. */
.alert-error {
  position: relative;
  padding: var(--bs-alert-padding-y) var(--bs-alert-padding-x);
  margin-bottom: var(--bs-alert-margin-bottom);
  border: 1px solid var(--ipa-critical);
  border-left-width: 3px;
  border-radius: var(--ipa-radius-lg);
  background-color: var(--ipa-critical-subtle);
  color: var(--ipa-text-secondary);
}

/* Tables — general reset toward "quiet" */
.table {
  --bs-table-bg: transparent;
  --bs-table-color: var(--ipa-text);
  --bs-table-border-color: var(--ipa-border);
  --bs-table-hover-bg: var(--ipa-surface-hover);
  --bs-table-hover-color: var(--ipa-text);
}

/* --------------------------------------------------------------------------
   7. Navbar
   -------------------------------------------------------------------------- */

/* The chrome is the same dark frame in both themes — see --ipa-chrome-* in §1.
   No border-bottom: against a white page the colour change is the edge. */
.ipa-navbar {
  /* Set Bootstrap's own navbar variables, not just the nav-link ones. An open
     dropdown is styled by `.navbar-nav .show > .nav-link`, which reads
     --bs-navbar-active-color — that defaults to near-black and turned the
     trigger unreadable against the dark chrome the moment you clicked it. */
  --bs-navbar-color:        var(--ipa-chrome-text);
  --bs-navbar-hover-color:  var(--ipa-chrome-text-bold);
  --bs-navbar-active-color: var(--ipa-chrome-text-bold);
  --bs-navbar-brand-color:  var(--ipa-chrome-text-bold);
  --bs-navbar-brand-hover-color: var(--ipa-chrome-accent);
  --bs-navbar-disabled-color: rgba(255, 255, 255, 0.4);
  background-color: var(--ipa-chrome-bg);
  /* A thin brand strip across the top of the frame. On top, not bottom, so it
     never meets the active-link underline. */
  border-top: 3px solid var(--ipa-purple-500);
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
}

.ipa-navbar .navbar-brand {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-weight: 650;
  font-size: var(--ipa-text-lg);
  letter-spacing: -0.02em;
  color: var(--ipa-chrome-text-bold);
}

.ipa-navbar .navbar-brand:hover { color: var(--ipa-chrome-accent); }
.ipa-navbar .navbar-brand img { display: block; }

.ipa-navbar .navbar-nav {
  --bs-nav-link-color: var(--ipa-chrome-text);
  --bs-nav-link-hover-color: var(--ipa-chrome-text-bold);
  --bs-nav-link-font-size: var(--ipa-text-base);
}

.ipa-navbar .nav-link {
  position: relative;
  font-weight: 500;
  padding-left: 0.75rem;
  padding-right: 0.75rem;
}

.ipa-navbar .nav-link.active {
  color: var(--ipa-chrome-text-bold);
}

/* Active marker: an underline rather than a colour swap.
   The offset has to keep the bar inside the navbar. It used to sit at -0.5rem
   and land against the navbar's border-bottom; with the border gone, that
   value clips the bar off the bottom edge. */
@media (min-width: 768px) {
  .ipa-navbar .nav-item > .nav-link.active::after {
    content: "";
    position: absolute;
    left: 0.75rem;
    right: 0.75rem;
    bottom: -0.3125rem;
    height: 2px;
    border-radius: 2px 2px 0 0;
    background-color: var(--ipa-chrome-accent);
  }
}

.ipa-navbar .navbar-toggler {
  border-color: var(--ipa-chrome-border);
  padding: 0.25rem 0.5rem;
}

.ipa-navbar .navbar-toggler:focus { box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.25); }

/* Stroke colour is baked into the data-URI, so it cannot use a token. It has
   to match --ipa-chrome-text (#ced4da). One rule for both themes now that the
   chrome no longer flips. */
.ipa-navbar .navbar-toggler-icon {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='%23ced4da' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
}

/* Theme switcher */
.ipa-theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--ipa-radius-md);
  background: none;
  color: var(--ipa-chrome-text);
  font-size: var(--ipa-text-md);
  line-height: 1;
}

.ipa-theme-toggle:hover {
  background-color: var(--ipa-chrome-hover);
  color: var(--ipa-chrome-text-bold);
}

.ipa-theme-toggle::after { display: none; }

/* --------------------------------------------------------------------------
   8. The feed — shared signal system
   -------------------------------------------------------------------------- */

/* Ticker code */
.ipa-ticker {
  display: inline-block;
  padding: 0.125rem 0.4375rem;
  border: 1px solid var(--ipa-border-strong);
  border-radius: var(--ipa-radius-sm);
  /* Neutral and outlined, not lavender. The code is the column you scan down,
     so it gets the darkest text on the row; on a pale purple tint it sank into
     the stripes and the header band. */
  background-color: var(--ipa-surface-raised);
  color: var(--ipa-heading);
  font-size: var(--ipa-text-xs);
  font-weight: 700;
  letter-spacing: 0.03em;
  text-decoration: none;
  white-space: nowrap;
}

a.ipa-ticker:hover {
  border-color: var(--ipa-accent);
  background-color: var(--ipa-accent-subtle);
  color: var(--ipa-accent-hover);
}

/* Price-sensitive marker. Deliberately just the exclamation glyph: it appears
   on roughly a quarter of rows, so a labelled chip made feeds look like a
   fire alarm. */
.ipa-ps {
  color: var(--ipa-critical);
  font-size: 0.875em;
  vertical-align: 1px;
  margin-left: 0.3125rem;
}

.ipa-ps-label {
  display: inline-flex;
  align-items: center;
  gap: 0.3125rem;
  color: var(--ipa-critical);
  font-size: var(--ipa-text-sm);
  font-weight: 600;
}

.ipa-ps-label .ipa-ps { margin-left: 0; }

/* Small status pill — kept for any future non-price-sensitive signal */
.ipa-signal {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.0625rem 0.375rem;
  border-radius: var(--ipa-radius-sm);
  font-size: var(--ipa-text-xs);
  font-weight: 600;
  letter-spacing: 0.01em;
  white-space: nowrap;
  vertical-align: 1px;
}

.ipa-signal--critical {
  background-color: var(--ipa-critical-subtle);
  color: var(--ipa-critical);
}

/* Impact score */
.ipa-score {
  display: inline-block;
  min-width: 2.25rem;
  padding: 0.125rem 0.375rem;
  border-radius: var(--ipa-radius-sm);
  color: #fff;
  font-size: var(--ipa-text-xs);
  font-weight: 600;
  text-align: center;
}

/* Icon action buttons — bookmark, PDF, analysis. One language for all three. */
.ipa-actions {
  display: inline-flex;
  align-items: center;
  gap: 0.125rem;
  white-space: nowrap;
}

/* Fixed slots. Right-aligned flex moved every icon whenever an optional one
   (the AI summary) was absent, so the bookmark sat in a different place from
   row to row and there was nothing to aim at. Each action now owns a column
   and keeps its x position whether or not its neighbours exist. */
.ipa-actions--slots {
  display: inline-grid;
  grid-template-columns: repeat(3, 1.75rem);
  align-items: center;
  justify-items: center;
  gap: 0;
}

/* Slots are positional, not semantic, because which action is optional differs
   by feed: on Announcements it is the AI summary, on Fast Feed it is the PDF.
   Each template puts its own optional action in the last slot, so the empty
   column always falls at the end of the row rather than leaving a gap between
   the two actions that are nearly always present. */
.ipa-actions--slots > .ipa-slot-1 { grid-column: 1; }
.ipa-actions--slots > .ipa-slot-2 { grid-column: 2; }
.ipa-actions--slots > .ipa-slot-3 { grid-column: 3; }

.ipa-iconbtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  padding: 0;
  border: 0;
  border-radius: var(--ipa-radius-md);
  background: none;
  /* Accented at rest, not grey: these are the only controls in a feed row, and
     in muted grey they read as decoration rather than as things you can click. */
  color: var(--ipa-accent);
  font-size: 0.9375rem;
  line-height: 1;
  text-decoration: none;
  transition: background-color 0.12s ease, color 0.12s ease;
}

.ipa-iconbtn:hover,
.ipa-iconbtn:focus-visible {
  background-color: var(--ipa-surface-inset);
  color: var(--ipa-accent-hover);
}

/* Bookmarked state is carried by the glyph itself — bookmarks.js swaps
   bi-bookmark for bi-bookmark-fill — so it survives the resting colour now
   being the accent too. */
.ipa-iconbtn.is-active { color: var(--ipa-accent); }

/* Feed table */
.ipa-feed {
  width: 100%;
  margin-bottom: 0;
  border-collapse: collapse;
}

.ipa-feed thead th {
  padding: 0.4375rem 0.75rem;
  border-bottom: 2px solid var(--ipa-border-strong);
  background-color: var(--ipa-surface-header);
  color: var(--ipa-heading);
  font-size: var(--ipa-text-xs);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
}

.ipa-feed tbody tr {
  border-bottom: 1px solid var(--ipa-border);
}

/* Zebra striping. Alternating bands let the eye carry a row across to the
   headline and action columns without losing its place.
   The row rule STAYS under striping. Dropping it left the white bands with no
   separator at all, which is most of why the table read as an undifferentiated
   block — the stripe alone is too light to carry a row on its own. */
.ipa-feed--striped tbody tr:nth-child(even) {
  background-color: var(--ipa-surface-stripe);
}

/* After the stripe rule (equal specificity), so hover wins on both bands. */
.ipa-feed tbody tr:hover { background-color: var(--ipa-surface-inset); }

.ipa-feed td {
  padding: 0.6875rem 0.75rem;
  vertical-align: baseline;
}

/* Every cell keeps the same horizontal padding, so the "Code" header sits
   directly above the ticker badges rather than flush against the page edge.
   The table is pulled out by the same amount, so rows still line up with the
   rest of the page. */
.ipa-feed { margin-left: -0.75rem; margin-right: -0.75rem; width: calc(100% + 1.5rem); }

.ipa-feed .ipa-feed-code { width: 1%; white-space: nowrap; }

.ipa-feed .ipa-feed-time {
  width: 1%;
  white-space: nowrap;
}

.ipa-feed td.ipa-feed-time { color: var(--ipa-text-muted); }

/* On Events the time is the point of the row, not metadata about it, so it
   keeps body colour; only the conversion to the event's own zone is muted. */
.ipa-feed--events td.ipa-feed-time { color: var(--ipa-text); }

/* Secondary text beside a headline — an event's host domain. Lighter and
   smaller as well as fainter: in faint alone, at the headline's weight, it
   read as part of the title. */
.ipa-feed td.ipa-feed-title .ipa-faint {
  margin-left: 0.25rem;
  font-size: var(--ipa-text-sm);
  font-weight: 400;
}

/* Ticker → headline → date. Weight, not just colour, sets the reading order. */
.ipa-feed td.ipa-feed-title { font-weight: 500; }

.ipa-feed .ipa-feed-title a {
  color: var(--ipa-text);
  text-decoration: none;
}

.ipa-feed .ipa-feed-title a:hover { color: var(--ipa-accent); }

/* Every headline links to its announcement page, summary or not. When only the
   summarised minority were links (in accent), the rest looked broken beside
   them. Headlines stay in body colour so the list doesn't turn purple; the
   robot icon is what marks a summary. */
.ipa-feed .ipa-feed-title a.ipa-headline:hover {
  color: var(--ipa-accent-hover);
  text-decoration: underline;
}

.ipa-feed .ipa-feed-score { width: 1%; white-space: nowrap; }

.ipa-feed .ipa-feed-actions {
  width: 1%;
  text-align: right;
  vertical-align: middle;
}

/* Date-group separator row (Events).
   A tinted band with an accent rule, so days read as distinct blocks rather
   than as another row of grey uppercase text. */
.ipa-feed .ipa-feed-group { border-bottom: 0; }

.ipa-feed .ipa-feed-group td {
  padding: 0.4375rem 0.75rem;
  border-top: 1px solid var(--ipa-border);
  /* Identical to the column headings on Announcements — band colour, 2px rule
     and text colour — so a date reads as a header of the same kind. That
     surface also sits a clear step above --ipa-surface-stripe, which is what
     made the old accent tint disappear into the zebra bands. The text used to
     stay accent purple; beside Announcements that read as a mismatch. */
  border-bottom: 2px solid var(--ipa-border-strong);
  background-color: var(--ipa-surface-header);
  color: var(--ipa-heading);
  font-size: var(--ipa-text-xs);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* Only the very first band sits flush against the top of the table. Each day
   now has its own tbody, so this has to key off the tbody rather than the
   row, which is :first-child in every one of them. */
.ipa-feed tbody:first-child .ipa-feed-group td { border-top: 0; }



/* Mobile reflow. One layout, driven by CSS — no duplicated markup. */
@media (max-width: 575.98px) {
  .ipa-feed thead { display: none; }

  /* Four columns, so feeds with an impact-score cell (Roundup Max) and feeds
     without one (Announcements, Events) share a single definition. An absent
     cell simply leaves its column at zero width. */
  .ipa-feed tbody tr {
    display: grid;
    grid-template-columns: auto auto 1fr auto;
    grid-template-areas:
      "code time score actions"
      "title title title title";
    gap: 0.25rem 0.5rem;
    padding: 0.75rem 0;
    align-items: center;
  }

  .ipa-feed { margin-left: 0; margin-right: 0; width: 100%; }
  .ipa-feed td { padding: 0 !important; }

  /* Stripes run edge to edge on mobile, so the rows get their padding back
     and the table is pulled out by the same amount to stay aligned. */
  .ipa-feed--striped { margin-left: -0.5rem; margin-right: -0.5rem; width: calc(100% + 1rem); }
  .ipa-feed--striped tbody tr { padding-left: 0.5rem; padding-right: 0.5rem; }
  .ipa-feed .ipa-feed-code    { grid-area: code;    width: auto; }
  .ipa-feed .ipa-feed-time    { grid-area: time;    width: auto; }
  .ipa-feed .ipa-feed-score   { grid-area: score;   width: auto; }
  .ipa-feed .ipa-feed-title   { grid-area: title; }
  .ipa-feed .ipa-feed-actions { grid-area: actions; width: auto; }

  .ipa-feed .ipa-feed-group {
    display: block;
    padding-bottom: 0;
    border-bottom: 0;
  }

  /* A table-cell inside a display:block row shrink-wraps to its text, which
     leaves the date band as a stub rather than a full-width header. Making
     the cell a block spans it, and it carries its own padding since the
     mobile reset zeroes cell padding. */
  .ipa-feed .ipa-feed-group td {
    display: block;
    padding: 0.4375rem 0.5rem !important;
  }

  /* The band is full-bleed across the pulled-out table, so the row padding
     that insets ordinary rows would double up here. */
  .ipa-feed--striped tbody tr.ipa-feed-group {
    padding-left: 0;
    padding-right: 0;
  }
}

/* --------------------------------------------------------------------------
   9. Article list (Fast Feed, Roundup references)
   -------------------------------------------------------------------------- */

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

.ipa-list > li {
  padding: 1.125rem 0;
  border-bottom: 1px solid var(--ipa-border);
}

.ipa-list > li:first-child { padding-top: 0.25rem; }
.ipa-list > li:last-child  { border-bottom: 0; }

/* Striped variant (Fast Feed). Items here are tall enough that a hairline
   rule is easy to lose track of; a band tells you at a glance where one
   summary ends and the next begins. Bands are inset-padded and the list is
   pulled out to match, so text stays aligned with the rest of the page. */
.ipa-list--striped {
  margin-left: -0.875rem;
  margin-right: -0.875rem;
}

/* As with the feed table, the rule stays: the band alone is too light to mark
   where one item ends, and these items are tall. */
.ipa-list--striped > li {
  padding: 1.125rem 0.875rem;
  border-radius: var(--ipa-radius-md);
}

/* The container gutter is only 0.75rem on phones, so the full 0.875rem pull-out
   ran 2px past the viewport and the page scrolled sideways. Same inset as the
   striped feed table at this width. */
@media (max-width: 575.98px) {
  .ipa-list--striped { margin-left: -0.5rem; margin-right: -0.5rem; }
  .ipa-list--striped > li { padding-left: 0.5rem; padding-right: 0.5rem; }
}

.ipa-list--striped > li:first-child { padding-top: 1.125rem; }
.ipa-list--striped > li:nth-child(even) {
  background-color: var(--ipa-surface-stripe);
}

/* Compact variant — a reference list, not an article list. One row per item. */
.ipa-list--compact > li {
  display: grid;
  grid-template-columns: auto auto 1fr auto;
  align-items: baseline;
  gap: 0.5rem;
  padding: 0.5rem 0;
}

.ipa-list--compact > li:first-child { padding-top: 0.5rem; }

.ipa-list--compact .ipa-item-title {
  margin: 0;
  font-size: var(--ipa-text-base);
  font-weight: 500;
  line-height: 1.45;
}

.ipa-list--compact .ipa-item-meta {
  margin-left: 0;
  align-self: baseline;
}

@media (max-width: 575.98px) {
  .ipa-list--compact > li {
    grid-template-columns: auto auto 1fr;
    row-gap: 0.25rem;
  }
  .ipa-list--compact .ipa-item-title { grid-column: 1 / -1; }
}

.ipa-item-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin-bottom: 0.375rem;
}

.ipa-item-meta {
  margin-left: auto;
  white-space: nowrap;
  /* Muted, as the release time is in the announcement table. */
  color: var(--ipa-text-muted);
  font-size: var(--ipa-text-sm);
}

/* Ticker, headline and price-sensitive marker share one line, with the
   timestamp pushed right — matching the announcements feed and the roundup
   references. Below 576px there isn't room, so it stacks. */
.ipa-item-head--inline {
  align-items: baseline;
  flex-wrap: nowrap;
  margin-bottom: 0.5rem;
}

.ipa-item-head--inline .ipa-item-title {
  flex: 1 1 auto;
  min-width: 0;
  margin-bottom: 0;
}

.ipa-item-head--inline .ipa-ticker { flex: 0 0 auto; }
.ipa-item-head--inline .ipa-item-meta { flex: 0 0 auto; }

@media (max-width: 575.98px) {
  /* Areas are named explicitly. Left to auto-placement, the ticker landed in a
     column sized by the timestamp and the badge stretched to fill it, while the
     timestamp fell to a third row. */
  .ipa-item-head--inline {
    display: grid;
    grid-template-columns: auto 1fr;
    grid-template-areas:
      "code  meta"
      "title title";
    align-items: center;
    column-gap: 0.5rem;
    row-gap: 0.25rem;
  }
  .ipa-item-head--inline .ipa-ticker    { grid-area: code;  justify-self: start; }
  .ipa-item-head--inline .ipa-item-meta { grid-area: meta;  justify-self: end; margin-left: 0; }
  .ipa-item-head--inline .ipa-item-title { grid-area: title; }
}

.ipa-item-title {
  margin: 0 0 0.375rem;
  font-size: var(--ipa-text-md);
  font-weight: 600;
  line-height: 1.4;
}

.ipa-item-title a { color: var(--ipa-heading); text-decoration: none; }
.ipa-item-title a:hover { color: var(--ipa-accent); }

.ipa-item-body {
  color: var(--ipa-text);
  font-size: var(--ipa-text-base);
}

.ipa-item-body > :last-child { margin-bottom: 0; }

.ipa-item-body ul {
  margin-bottom: 0.5rem;
  padding-left: 1.125rem;
}

.ipa-item-body li { margin-bottom: 0.25rem; }

.ipa-item-foot {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-top: 0.625rem;
  font-size: var(--ipa-text-sm);
}

/* The chevron is what makes this read as a control rather than a caption —
   it's the primary interaction on Fast Feed, and muted text alone didn't say
   so. Bootstrap flips aria-expanded on the trigger, so the rotation needs no
   JavaScript of ours. */
.ipa-disclosure {
  display: inline-flex;
  align-items: center;
  gap: 0.3125rem;
  color: var(--ipa-text-muted);
  font-size: var(--ipa-text-sm);
  text-decoration: none;
}

.ipa-disclosure .bi {
  font-size: 0.75em;
  transition: transform 0.15s ease;
}

.ipa-disclosure[aria-expanded="true"] .bi { transform: rotate(180deg); }

.ipa-disclosure:hover { color: var(--ipa-accent); }

/* --------------------------------------------------------------------------
   10. Page furniture
   -------------------------------------------------------------------------- */

#content {
  padding-top: 1.75rem;
  padding-bottom: 2.5rem;
}

.ipa-page-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--ipa-space-4);
  flex-wrap: wrap;
  margin-bottom: var(--ipa-space-4);
}

.ipa-page-title {
  margin: 0;
  font-size: var(--ipa-text-xl);
  font-weight: 650;
  letter-spacing: -0.02em;
}

.ipa-toolbar {
  display: flex;
  align-items: center;
  gap: var(--ipa-space-3);
  flex-wrap: wrap;
  margin-bottom: var(--ipa-space-4);
}

/* A toolbar row with another directly beneath it (search, then filter). */
.ipa-toolbar--stacked {
  margin-bottom: var(--ipa-space-3);
  /* Search and its tips icon only: let the field shrink rather than wrap, or
     on phones the icon drops onto a line of its own. */
  flex-wrap: nowrap;
}

.ipa-toolbar--stacked .ipa-search { min-width: 0; }

/* A standing caveat beside the filters — small and faint, so it is there to be
   read without reading as an alert on every visit. */
.ipa-toolbar-note {
  display: flex;
  align-items: baseline;
  gap: 0.375rem;
  margin: 0 0 0 auto;
  color: var(--ipa-text-faint);
  font-size: var(--ipa-text-xs);
}

.ipa-search {
  position: relative;
  flex: 1 1 22rem;
  max-width: 32rem;
}

.ipa-search .form-control { padding-left: 2.25rem; }

.ipa-search .ipa-search-icon {
  position: absolute;
  top: 50%;
  left: 0.75rem;
  transform: translateY(-50%);
  color: var(--ipa-text-faint);
  pointer-events: none;
  font-size: var(--ipa-text-base);
}

.ipa-empty {
  padding: var(--ipa-space-7) var(--ipa-space-4);
  color: var(--ipa-text-muted);
  text-align: center;
}

.ipa-pagination-wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ipa-space-4);
  flex-wrap: wrap;
  margin-top: var(--ipa-space-5);
}

.ipa-pagination-status {
  color: var(--ipa-text-faint);
  font-size: var(--ipa-text-sm);
}

/* Announcement detail header */
.ipa-detail-header {
  padding-bottom: var(--ipa-space-4);
  margin-bottom: var(--ipa-space-5);
  border-bottom: 1px solid var(--ipa-border);
}

.ipa-detail-title {
  margin: 0.5rem 0 0.25rem;
  font-size: var(--ipa-text-2xl);
  font-weight: 650;
  letter-spacing: -0.022em;
  max-width: 46rem;
}

.ipa-detail-company {
  margin: 0;
  color: var(--ipa-text-muted);
  font-size: var(--ipa-text-sm);
}

.ipa-detail-actions {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin-top: var(--ipa-space-4);
}

.bookmark-btn.is-active { color: var(--ipa-accent); }

/* Company profile: key figures under the name, as a row of label/value pairs. */
.ipa-stats {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ipa-space-3) var(--ipa-space-6);
  margin: var(--ipa-space-4) 0 0;
}

.ipa-stats dt {
  color: var(--ipa-text-faint);
  font-size: var(--ipa-text-xs);
  font-weight: 600;
}

.ipa-stats dd {
  margin: 0;
  color: var(--ipa-heading);
  font-size: var(--ipa-text-lg);
  font-weight: 650;
  font-variant-numeric: tabular-nums;
}

/* Companies directory: marks a stock the viewer already follows. */
.ipa-following {
  color: var(--ipa-accent);
  font-size: 0.875em;
  margin-left: 0.3125rem;
}

/* "Sign in to see more" under a capped list. */
.ipa-more {
  margin-top: var(--ipa-space-3);
  color: var(--ipa-text-muted);
  font-size: var(--ipa-text-sm);
}

/* Company profile: most-followed peers in the same sector. A ranked list, so
   it fills down each column rather than across, and reads in rank order. */
.ipa-peers {
  columns: 15rem;
  column-gap: var(--ipa-space-5);
  margin: 0;
  padding: 0;
  list-style: none;
}

.ipa-peers li {
  margin-bottom: var(--ipa-space-2);
  break-inside: avoid;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--ipa-text-sm);
}

/* Announcement detail reads like the roundup: one centred column at the same
   measure, the analysis as running text rather than a boxed card. */
.ipa-editorial { max-width: 54rem; margin-left: auto; margin-right: auto; }

.ipa-editorial > section { margin-top: var(--ipa-space-6); }

.ipa-analysis-lead {
  margin-bottom: var(--ipa-space-5);
  color: var(--ipa-heading);
  font-size: var(--ipa-text-lg);
  font-weight: 600;
  line-height: 1.45;
  text-wrap: pretty;
}

.ipa-analysis .ipa-section-title { margin-top: var(--ipa-space-5); }

.ipa-analysis-body { line-height: 1.7; }
.ipa-analysis-body > :last-child { margin-bottom: 0; }
.ipa-analysis-body ul { padding-left: 1.25rem; }
.ipa-analysis-body li { margin-bottom: 0.375rem; }

.ipa-analysis-note,
.ipa-empty-note {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  color: var(--ipa-text-faint);
  font-size: var(--ipa-text-sm);
}

.ipa-analysis-note {
  margin-top: var(--ipa-space-5);
  padding-top: var(--ipa-space-3);
  border-top: 1px solid var(--ipa-border);
  font-size: var(--ipa-text-xs);
}

.ipa-empty-note {
  padding: var(--ipa-space-4);
  border: 1px dashed var(--ipa-border-strong);
  border-radius: var(--ipa-radius-lg);
  color: var(--ipa-text-muted);
}

/* --------------------------------------------------------------------------
   10b. Daily Roundup — an editorial page, narrower than the app pages
   -------------------------------------------------------------------------- */

/* The roundup is editorial. The story keeps a readable measure; the references
   sit below it (not floating alongside) but flow into columns so the list stays
   short instead of turning into a long scroll. */
.ipa-roundup { max-width: 54rem; margin: 0 auto; }

.ipa-roundup-aside { margin-top: var(--ipa-space-6); }

.ipa-roundup-header {
  padding-bottom: var(--ipa-space-4);
  margin-bottom: var(--ipa-space-4);
  border-bottom: 1px solid var(--ipa-border);
}

.ipa-roundup-title {
  margin: 0 0 0.25rem;
  font-size: var(--ipa-text-2xl);
  font-weight: 700;
  letter-spacing: -0.026em;
}

.ipa-roundup-sub {
  margin: 0;
  color: var(--ipa-text-muted);
  font-size: var(--ipa-text-sm);
}

.ipa-roundup-nav {
  display: flex;
  justify-content: space-between;
  gap: var(--ipa-space-3);
  margin-bottom: var(--ipa-space-4);
}

.ipa-chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3125rem;
  margin-bottom: var(--ipa-space-5);
}

.ipa-roundup-body {
  max-width: none;
  color: var(--ipa-text);
  line-height: 1.7;
}

/* Story headlines — see promote_roundup_headlines in views.py. A clear step
   up from body text, with the space above rather than below, so each heading
   binds to the story it opens. */
.ipa-roundup-body h2,
.ipa-roundup-body h3 {
  margin-top: var(--ipa-space-6);
  margin-bottom: var(--ipa-space-2);
  color: var(--ipa-heading);
  font-size: var(--ipa-text-lg);
  font-weight: 650;
  line-height: 1.35;
}

.ipa-roundup-body > :first-child { margin-top: 0; }

.ipa-roundup-refs { margin-top: 0; }

.ipa-section-title {
  padding-bottom: 0.5rem;
  margin-bottom: var(--ipa-space-3);
  /* Same treatment as the table column headings: dark label, heavy rule. */
  border-bottom: 2px solid var(--ipa-border-strong);
  color: var(--ipa-heading);
  font-size: var(--ipa-text-xs);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* --------------------------------------------------------------------------
   11. Footer — quiet, so it never outweighs the page content
   -------------------------------------------------------------------------- */

/* Closes the frame the navbar opens — same dark chrome, both themes. */
.ipa-footer {
  margin-top: var(--ipa-space-7);
  padding: var(--ipa-space-3) 0;
  background-color: var(--ipa-chrome-bg);
  color: var(--ipa-chrome-text);
  font-size: var(--ipa-text-sm);
}

.ipa-footer-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ipa-space-4);
  flex-wrap: wrap;
}

.ipa-footer-links {
  display: flex;
  align-items: center;
  gap: var(--ipa-space-4);
  flex-wrap: wrap;
}

.ipa-footer a {
  color: var(--ipa-chrome-text);
  text-decoration: none;
}

.ipa-footer a:hover {
  color: var(--ipa-chrome-text-bold);
  text-decoration: underline;
}

/* --------------------------------------------------------------------------
   12. Stock autocomplete (replaces jQuery UI)
   -------------------------------------------------------------------------- */

.ipa-ac-wrap { position: relative; }

.ipa-ac-menu {
  position: absolute;
  z-index: 1060;
  top: 100%;
  left: 0;
  right: 0;
  max-height: 15rem;
  margin-top: 0.25rem;
  overflow-y: auto;
  list-style: none;
  padding: 0.25rem;
  border: 1px solid var(--ipa-border);
  border-radius: var(--ipa-radius-lg);
  background-color: var(--ipa-surface-raised);
  box-shadow: var(--ipa-shadow-md);
}

.ipa-ac-menu[hidden] { display: none; }

.ipa-ac-option {
  padding: 0.4375rem 0.625rem;
  border-radius: var(--ipa-radius-md);
  color: var(--ipa-text-secondary);
  font-size: var(--ipa-text-sm);
  cursor: pointer;
}

.ipa-ac-option[aria-selected="true"],
.ipa-ac-option:hover {
  background-color: var(--ipa-accent-subtle);
  color: var(--ipa-accent);
}

.ipa-ac-option .ipa-ac-code { font-weight: 600; }

/* --------------------------------------------------------------------------
   13. Utilities
   -------------------------------------------------------------------------- */

.ipa-muted   { color: var(--ipa-text-muted) !important; }
.ipa-faint   { color: var(--ipa-text-faint) !important; }
.ipa-hairline { border-color: var(--ipa-border) !important; }

.ipa-prose { max-width: 68ch; }

/* Small forms — login, registration, preferences. */
.ipa-narrow { max-width: 26rem; }

/* My Stocks. The watchlist is one long comma-separated list of codes, so the
   field wants the full width of the page, not a prose measure. */
.ipa-stocks-form { max-width: none; }

.ipa-stocks-form textarea.form-control,
.ipa-stocks-form input.form-control {
  width: 100%;
  min-height: 9rem;
  line-height: 1.7;
  font-variant-numeric: tabular-nums;
}

/* An inline icon action sitting inside a line of text. */
.ipa-iconbtn--inline {
  width: 1.25rem;
  height: 1.25rem;
  font-size: 0.8125rem;
  vertical-align: baseline;
}

.ipa-prose p { margin-bottom: 0.875rem; }

/* Respect users who ask for less motion. */
@media (prefers-reduced-motion: reduce) {
  * { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
}
