/* ==========================================================================
   Teljoiner design system
   --------------------------------------------------------------------------
   Loaded last, on every page, on top of the purchased Eduport theme
   (assets/css/style.css, Bootstrap 5.1.3). The theme stays where it is: it
   carries the grid, the utilities and a hundred components this site uses.
   What it does not carry is a consistent voice - every page had picked its
   own type sizes, its own greys and its own button weights, so the site read
   as a dozen sites stitched together.

   This file is that missing layer. It sets one palette, one type scale and
   one spacing rhythm, then normalises the handful of components that appear
   on every page: buttons, cards, forms, badges, tables, sections.

   Order matters. Everything here is written to win against the theme by
   specificity or by later source order, never with !important unless the
   theme itself used !important first.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Tokens
   --------------------------------------------------------------------------
   Neutrals carry a slight blue bias so they sit with the brand rather than
   fighting it; a pure grey next to a blue accent reads as unconsidered.
   -------------------------------------------------------------------------- */
:root {
  /* ink */
  --tj-ink:        #101826;   /* headings, primary text on light */
  --tj-body:       #414e63;   /* running text */
  --tj-muted:      #5b6472;   /* secondary text - 4.9:1 on white */
  /* Was #8a94a6, which measures 3.06:1 on white. It was documented as
     decorative but had quietly become the colour of timestamps, view counts
     and card subtitles - all real text. Darkened until it passes. */
  --tj-faint:      #6c7482;   /* 4.7:1 on white */

  /* ground */
  --tj-surface:    #ffffff;
  --tj-ground:     #f5f7fb;   /* alternating section ground */
  --tj-sunken:     #eef1f7;   /* wells, inactive chips */
  --tj-line:       #e3e7ee;
  --tj-line-soft:  #eef1f6;

  /* --- Telegram's own palette -------------------------------------------
     Not "a blue that feels Telegram-ish": these are the values Telegram
     ships. #2AABEE -> #229ED9 is the logo gradient, and the six hues after
     it are the gradients Telegram assigns to user avatars. Using one
     published set is what lets the site be loud without turning into a paint
     chart - the colours were harmonised by the people who made them.

     Three tones per hue, because one is never enough:
       -bright  saturated. Fills, and text on the dark hero, where every one
                of these clears 7.5:1.
       (base)   fills and large type on light grounds.
       -ink     the only tone allowed to carry small text. Each clears 4.5:1
                on white AND on its own wash - measured, not guessed.
       -wash    a tinted ground the -ink tone stays readable on.
  */
  --tg-blue-bright:   #2AABEE;
  /* The label colour for anything filled with the bright blue. White on it
     is 2.6:1; this is 6.2:1. */
  --tg-on-blue:       #052536;
  --tg-blue:          #229ED9;
  --tg-blue-ink:      #19749f;
  --tg-blue-wash:     #e3f4fd;

  --tg-cyan-bright:   #53EDD6;
  --tg-cyan:          #28C9B7;
  --tg-cyan-ink:      #187b70;
  --tg-cyan-wash:     #e9fdfa;

  --tg-green-bright:  #9AD164;
  --tg-green:         #46BA43;
  --tg-green-ink:     #30802e;
  --tg-green-wash:    #f2f9eb;

  --tg-orange-bright: #FEBB5B;
  --tg-orange:        #F68136;
  --tg-orange-ink:    #aa5925;
  --tg-orange-wash:   #fff6ea;

  --tg-violet-bright: #B694F9;
  --tg-violet:        #6C61DF;
  --tg-violet-ink:    #665bd2;
  --tg-violet-wash:   #f6f1fe;

  --tg-pink-bright:   #FF8AAC;
  --tg-pink:          #D95574;
  --tg-pink-ink:      #b44760;
  --tg-pink-wash:     #fff0f4;

  /* The logo gradient, for anything that should read as "this is Telegram". */
  --tg-gradient: linear-gradient(135deg, var(--tg-blue-bright) 0%, var(--tg-blue) 100%);

  /* brand - Telegram's blue now, not a corporate one */
  --tj-brand:      var(--tg-blue);
  --tj-brand-dark: #1b86b8;
  --tj-brand-ink:  var(--tg-blue-ink);
  --tj-brand-wash: var(--tg-blue-wash);

  /* --- second and third accents ----------------------------------------
     One hue carrying an entire site is what makes it read as dry: every
     section, every card and every button spoke in the same blue, so nothing
     could stand out by colour and everything had to stand out by size.

     These are not decoration. Each one owns a meaning, and that is the only
     reason a page is allowed to use it:

       blue  - commerce. Prices, buy buttons, paid packages.
       teal  - growth. Delivery, counts going up, progress, "it worked".
       amber - the gift. The free tier and nothing else, so the one thing we
               give away never looks like the things we sell.
  */
  --tj-teal:        var(--tg-cyan-ink);
  --tj-teal-bright: var(--tg-cyan);
  --tj-teal-wash:   var(--tg-cyan-wash);
  --tj-teal-deep:   #13655c;

  --tj-amber:        var(--tg-orange-ink);
  --tj-amber-bright: var(--tg-orange);
  --tj-amber-wash:   var(--tg-orange-wash);
  --tj-amber-deep:   #7d4119;

  /* semantic - kept separate from the accent so state never reads as brand */
  --tj-good:       #0f7a53;
  --tj-good-wash:  #e6f6ee;
  --tj-warn:       #9a5b00;
  --tj-warn-wash:  #fdf3e2;
  --tj-bad:        #b42318;
  --tj-bad-wash:   #fdecea;

  /* form */
  --tj-radius:     .625rem;
  --tj-radius-sm:  .375rem;
  --tj-radius-lg:  1rem;
  --tj-shadow:     0 1px 2px rgba(16, 24, 38, .04), 0 4px 16px rgba(16, 24, 38, .06);
  --tj-shadow-lg:  0 2px 6px rgba(16, 24, 38, .05), 0 16px 40px rgba(16, 24, 38, .10);
  --tj-focus:      0 0 0 3px rgba(29, 111, 224, .28);

  /* rhythm */
  --tj-section-y:  4.5rem;
  --tj-gutter:     1rem;

  /* bridge the theme's own variables so components we do not restyle still
     land on the new palette */
  --bs-body-font-family: "Public Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --bs-body-font-size: 1rem;
  --bs-body-color: var(--tj-body);
  --bs-primary: var(--tj-brand);
  --bs-primary-rgb: 29, 111, 224;
  --bs-link-color: var(--tj-brand);
  --bs-link-hover-color: var(--tj-brand-dark);
  --bs-border-color: var(--tj-line);
}

/* --------------------------------------------------------------------------
   2. Type
   --------------------------------------------------------------------------
   Plus Jakarta Sans for display, Public Sans for everything that is read.
   The theme shipped Heebo (a Hebrew-first face) over Roboto at 15px, which
   is why the site read as a template: no voice, and body copy a notch too
   small for the amount of product detail these pages carry.
   -------------------------------------------------------------------------- */
body {
  font-family: var(--bs-body-font-family);
  font-size: 1rem;
  line-height: 1.65;
  color: var(--tj-body);
  background: var(--tj-surface);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6,
.display-1, .display-2, .display-3, .display-4, .display-5, .display-6 {
  font-family: "Plus Jakarta Sans", var(--bs-body-font-family);
  color: var(--tj-ink);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -.015em;
  text-wrap: balance;
}

/* One scale, fluid between phone and desktop, so a heading never has to be
   re-sized per page. */
h1, .h1 { font-size: clamp(2rem, 1.35rem + 2.1vw, 3rem); letter-spacing: -.022em; }
h2, .h2 { font-size: clamp(1.6rem, 1.2rem + 1.3vw, 2.25rem); letter-spacing: -.02em; }
h3, .h3 { font-size: clamp(1.3rem, 1.1rem + .7vw, 1.625rem); }
h4, .h4 { font-size: 1.25rem; }
h5, .h5 { font-size: 1.0625rem; }
h6, .h6 { font-size: .9375rem; letter-spacing: 0; }

p { margin-bottom: 1rem; }
p:last-child { margin-bottom: 0; }

/* Running text stays near 65 characters; the site had full-width paragraphs
   at 1400px, which is roughly twice a comfortable measure. */
.measure   { max-width: 62ch; }
.measure-w { max-width: 78ch; }

/* Justified text was set on the homepage lede and pulled rivers of white
   space through it at every width. */
.text-justify, [style*="text-align: justify"], [style*="text-align:justify"] {
  text-align: start !important;
}

a { text-decoration: none; }
a:hover { text-decoration: underline; }
.btn, .nav-link, .navbar-brand, .dropdown-item, .badge, .page-link,
.card-title a, .list-group-item-action { text-decoration: none; }
.btn:hover, .nav-link:hover, .navbar-brand:hover,
.dropdown-item:hover, .page-link:hover { text-decoration: none; }

/* Bootstrap's .text-muted is #6c757d, 4.1:1 on white - below the 4.5:1 that
   makes small text readable for everyone. */
.text-muted { color: var(--tj-muted) !important; }
small, .small { font-size: .875rem; }

/* Figures that line up in a column should not dance as the digits change. */
.tabular, .price, .stat-value, td.num, th.num { font-variant-numeric: tabular-nums; }

.eyebrow {
  display: inline-block;
  font-size: .75rem;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--tj-brand);
  margin-bottom: .5rem;
}

.lede {
  font-size: 1.125rem;
  line-height: 1.65;
  color: var(--tj-muted);
  max-width: 62ch;
}

/* --------------------------------------------------------------------------
   3. Layout rhythm
   -------------------------------------------------------------------------- */
.section      { padding-block: var(--tj-section-y); }
.section-tight{ padding-block: 3rem; }
.section-ground {
  background:
    radial-gradient(44rem 22rem at 6% 0%, rgba(42, 171, 238, .13), transparent 68%),
    radial-gradient(40rem 20rem at 96% 100%, rgba(108, 97, 223, .10), transparent 68%),
    linear-gradient(180deg, #f3faff 0%, #ecf4fd 100%);
}
/* A ground that carries the growth accent instead of the neutral one, for
   sections about delivery rather than about price. */
.section-ground-teal {
  background:
    radial-gradient(46rem 24rem at 10% 0%, rgba(40, 201, 183, .20), transparent 70%),
    radial-gradient(40rem 22rem at 94% 96%, rgba(42, 171, 238, .15), transparent 70%),
    linear-gradient(180deg, #effdfa 0%, #e7f7fc 100%);
}

.section-head { margin-bottom: 2rem; }
.section-head > h2, .section-head > .h2 { margin-bottom: .5rem; }
.section-head > p { color: var(--tj-muted); margin-bottom: 0; max-width: 62ch; }
.section-head.text-center > p { margin-inline: auto; }

@media (max-width: 767.98px) {
  :root { --tj-section-y: 3rem; }
}

/* Nothing may push the page sideways. Wide tables, diagrams and code get
   their own scroller instead. */
html, body { overflow-x: clip; }
.table-responsive, .scroll-x { overflow-x: auto; }

/* --------------------------------------------------------------------------
   4. Buttons
   --------------------------------------------------------------------------
   The site had primary, outline, info, warning and link buttons all doing
   the job of "the main action on this page". One primary per view; the rest
   step down.
   -------------------------------------------------------------------------- */
.btn {
  --bs-btn-font-family: var(--bs-body-font-family);
  font-weight: 600;
  border-radius: var(--tj-radius-sm);
  padding: .625rem 1.125rem;
  line-height: 1.4;
  min-height: 2.75rem;               /* 44px - a comfortable touch target */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  transition: background-color .15s ease, border-color .15s ease, box-shadow .15s ease;
}
.btn-lg { padding: .8125rem 1.5rem; font-size: 1rem; min-height: 3.125rem; }
.btn-sm { padding: .4375rem .875rem; font-size: .875rem; min-height: 2.25rem; }

.btn:focus-visible { box-shadow: var(--tj-focus); outline: 0; }

.btn-primary {
  --bs-btn-bg: var(--tg-blue);
  /* Bootstrap paints the label from --bs-btn-color, so setting `color`
     alone left the spans inside the button white. */
  --bs-btn-color: var(--tg-on-blue);
  --bs-btn-hover-color: var(--tg-on-blue);
  --bs-btn-active-color: var(--tg-on-blue);
  --bs-btn-border-color: transparent;
  --bs-btn-hover-bg: var(--tj-brand-dark);
  --bs-btn-hover-border-color: transparent;
  --bs-btn-active-bg: var(--tj-brand-ink);
  --bs-btn-active-border-color: transparent;
  --bs-btn-disabled-bg: #a6d7ef;
  --bs-btn-disabled-color: #2b4d5c;
  --bs-btn-disabled-border-color: transparent;
  /* The logo gradient. A flat fill is what every bootstrap site has; this is
     the one thing Telegram owns outright after the paper plane. */
  background-image: var(--tg-gradient);
  border: 0;
  color: var(--tg-on-blue);
  box-shadow: 0 6px 18px -8px rgba(34, 158, 217, .75);
}
.btn-primary:hover {
  background-image: linear-gradient(135deg, #36b5f5 0%, #26a8e2 100%);
  box-shadow: 0 10px 26px -8px rgba(34, 158, 217, .9);
  color: var(--tg-on-blue);
}
.btn-primary:active, .btn-primary:focus { color: var(--tg-on-blue); }

/* Anything else the theme fills with the brand colour and labels in white
   has the same problem. */
.badge.bg-primary, a.badge.bg-primary, a.badge.bg-primary:hover,
.nav-pills .nav-link.active, .page-item.active .page-link {
  color: var(--tg-on-blue) !important;
  background: var(--tg-gradient) !important;
  border-color: transparent !important;
}
.btn-primary:disabled, .btn-primary.disabled {
  background-image: none;
  box-shadow: none;
}
.btn-outline-primary {
  --bs-btn-color: var(--tj-brand);
  --bs-btn-border-color: #b9d1f5;
  --bs-btn-hover-bg: var(--tj-brand-wash);
  --bs-btn-hover-border-color: var(--tj-brand);
  --bs-btn-hover-color: var(--tj-brand-ink);
}
/* The quiet neutral button - what "Cancel" and "Back" should be, instead of
   the solid grey .btn-secondary the site used, which competed with primary. */
.btn-light, .btn-quiet {
  --bs-btn-bg: var(--tj-sunken);
  --bs-btn-border-color: transparent;
  --bs-btn-color: var(--tj-ink);
  --bs-btn-hover-bg: #e3e8f1;
  --bs-btn-hover-border-color: transparent;
  --bs-btn-hover-color: var(--tj-ink);
}
.btn-secondary {
  --bs-btn-bg: var(--tj-sunken);
  --bs-btn-border-color: transparent;
  --bs-btn-color: var(--tj-ink);
  --bs-btn-hover-bg: #e3e8f1;
  --bs-btn-hover-border-color: transparent;
  --bs-btn-hover-color: var(--tj-ink);
}
.btn-success {
  --bs-btn-bg: var(--tj-good);
  --bs-btn-border-color: var(--tj-good);
  --bs-btn-hover-bg: #0c6243;
  --bs-btn-hover-border-color: #0c6243;
}

/* A button that is doing work should say so rather than look clickable. */
.btn.is-busy { pointer-events: none; opacity: .75; }

/* --------------------------------------------------------------------------
   5. Cards
   --------------------------------------------------------------------------
   One radius, one border, one shadow. Lift is spent on things that are
   actually interactive, not stamped on every block.
   -------------------------------------------------------------------------- */
.card {
  border: 1px solid var(--tj-line);
  border-radius: var(--tj-radius);
  background: var(--tj-surface);
  box-shadow: none;
}
.card.shadow, .card.shadow-sm { box-shadow: var(--tj-shadow) !important; border-color: var(--tj-line-soft); }
.card-header {
  background: transparent;
  border-bottom: 1px solid var(--tj-line);
  padding: 1rem 1.25rem;
  font-weight: 600;
  color: var(--tj-ink);
}
.card-footer {
  background: var(--tj-ground);
  border-top: 1px solid var(--tj-line);
  padding: .75rem 1.25rem;
  color: var(--tj-muted);
  font-size: .875rem;
}
.card-body { padding: 1.25rem; }
.card-title { color: var(--tj-ink); }

/* Only cards that lead somewhere get the hover lift. */
a.card, .card-link-wrap .card, .card.is-interactive {
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}
a.card:hover, .card-link-wrap:hover .card, .card.is-interactive:hover {
  transform: translateY(-2px);
  box-shadow: var(--tj-shadow-lg);
  border-color: #d4dcea;
}

/* --------------------------------------------------------------------------
   6. Forms
   -------------------------------------------------------------------------- */
.form-label {
  font-weight: 600;
  font-size: .875rem;
  color: var(--tj-ink);
  margin-bottom: .375rem;
}
.form-control, .form-select {
  border: 1px solid #cdd5e2;
  border-radius: var(--tj-radius-sm);
  padding: .625rem .875rem;
  font-size: 1rem;
  min-height: 2.75rem;
  color: var(--tj-ink);
  background-color: var(--tj-surface);
  transition: border-color .15s ease, box-shadow .15s ease;
}
.form-control::placeholder { color: #9aa4b5; }
.form-control:focus, .form-select:focus {
  border-color: var(--tj-brand);
  box-shadow: var(--tj-focus);
}
.form-control.is-invalid, .was-validated .form-control:invalid { border-color: var(--tj-bad); }
.form-control:disabled, .form-control[readonly] { background: var(--tj-sunken); color: var(--tj-muted); }
.form-text { color: var(--tj-muted); font-size: .8125rem; }
.input-group-text {
  background: var(--tj-sunken);
  border-color: #cdd5e2;
  color: var(--tj-muted);
  font-size: .9375rem;
}
.form-check-input:checked { background-color: var(--tj-brand); border-color: var(--tj-brand); }
.form-check-input:focus { box-shadow: var(--tj-focus); border-color: var(--tj-brand); }
.invalid-feedback { font-size: .8125rem; }

/* Keyboard focus must be visible everywhere, not only on form controls. */
a:focus-visible, button:focus-visible, [tabindex]:focus-visible,
.dropdown-item:focus-visible, .nav-link:focus-visible {
  outline: 2px solid var(--tj-brand);
  outline-offset: 2px;
  border-radius: var(--tj-radius-sm);
}

/* --------------------------------------------------------------------------
   7. Badges, pills, alerts
   --------------------------------------------------------------------------
   State reads at a glance as shape plus colour, never colour alone - a
   colour-blind reader gets the same information.
   -------------------------------------------------------------------------- */
.badge {
  font-weight: 600;
  font-size: .75rem;
  letter-spacing: .01em;
  padding: .3em .6em;
  border-radius: 999px;
}
.badge.bg-success { background: var(--tj-good-wash) !important; color: var(--tj-good) !important; }
.badge.bg-warning { background: var(--tj-warn-wash) !important; color: var(--tj-warn) !important; }
.badge.bg-danger  { background: var(--tj-bad-wash)  !important; color: var(--tj-bad)  !important; }
.badge.bg-info    { background: var(--tj-brand-wash)!important; color: var(--tj-brand-ink) !important; }
.badge.bg-primary { background: var(--tj-brand) !important; color: #fff !important; }
.badge.bg-light   { background: var(--tj-sunken) !important; color: var(--tj-ink) !important; }

.alert {
  border: 1px solid transparent;
  border-radius: var(--tj-radius);
  padding: 1rem 1.125rem;
  font-size: .9375rem;
}
.alert-info    { background: var(--tj-brand-wash); border-color: #cfe0fb; color: #103a72; }
.alert-success { background: var(--tj-good-wash);  border-color: #c5e8d6; color: #0b5c3e; }
.alert-warning { background: var(--tj-warn-wash);  border-color: #f2dcb4; color: #7a4700; }
.alert-danger  { background: var(--tj-bad-wash);   border-color: #f5cfcb; color: #8d1d14; }

/* --------------------------------------------------------------------------
   8. Tables
   -------------------------------------------------------------------------- */
.table { --bs-table-color: var(--tj-body); margin-bottom: 0; }
.table > :not(caption) > * > * { padding: .75rem 1rem; }
.table > thead th {
  font-size: .75rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--tj-muted);
  background: var(--tj-ground);
  border-bottom: 1px solid var(--tj-line);
  white-space: nowrap;
}
.table > tbody > tr > td { border-bottom: 1px solid var(--tj-line-soft); vertical-align: middle; }
.table > tbody > tr:last-child > td { border-bottom: 0; }

/* --------------------------------------------------------------------------
   9. Modals, dropdowns, tooltips
   -------------------------------------------------------------------------- */
.modal-content {
  border: 0;
  border-radius: var(--tj-radius-lg);
  box-shadow: var(--tj-shadow-lg);
  overflow: hidden;
}
.modal-header { border-bottom: 1px solid var(--tj-line); padding: 1rem 1.25rem; }
.modal-header.bg-primary, .modal-header.bg-info,
.modal-header.bg-success, .modal-header.bg-warning { border-bottom-color: transparent; }
.modal-body { padding: 1.25rem; }
.modal-footer { border-top: 1px solid var(--tj-line); padding: 1rem 1.25rem; background: var(--tj-ground); }
.modal-title { font-size: 1.0625rem; }

.dropdown-menu {
  border: 1px solid var(--tj-line);
  border-radius: var(--tj-radius);
  box-shadow: var(--tj-shadow-lg);
  padding: .375rem;
}
.dropdown-item {
  border-radius: var(--tj-radius-sm);
  padding: .5rem .75rem;
  font-size: .9375rem;
  color: var(--tj-body);
}
.dropdown-item:hover, .dropdown-item:focus { background: var(--tj-brand-wash); color: var(--tj-brand-ink); }
.dropdown-divider { border-color: var(--tj-line); margin: .375rem 0; }

/* --------------------------------------------------------------------------
   10. Empty states + skeletons
   --------------------------------------------------------------------------
   Several pages fell back to a camera icon on a third-party CDN when their
   illustration 404'd. These give every list one honest empty state.
   -------------------------------------------------------------------------- */
.tj-empty { max-width: 28rem; margin-inline: auto; text-align: center; padding-block: 2.5rem; }
.tj-empty .tj-empty-ico {
  width: 4rem; height: 4rem; margin: 0 auto 1.1rem;
  border-radius: 50%;
  background: var(--tj-sunken);
  color: var(--tj-faint);
  font-size: 1.75rem;
  display: flex; align-items: center; justify-content: center;
}
.tj-empty h3, .tj-empty h4, .tj-empty h5, .tj-empty h6 { margin-bottom: .35rem; }
.tj-empty p { color: var(--tj-muted); font-size: .9375rem; margin-bottom: 1.25rem; }

@keyframes tj-shimmer { to { background-position-x: -200%; } }
.tj-skeleton {
  background: linear-gradient(90deg, var(--tj-sunken) 40%, #f6f8fc 50%, var(--tj-sunken) 60%);
  background-size: 200% 100%;
  animation: tj-shimmer 1.4s linear infinite;
  border-radius: var(--tj-radius-sm);
  color: transparent;
}

/* --------------------------------------------------------------------------
   10b. Deferred rendering
   --------------------------------------------------------------------------
   content-visibility: auto lets the browser skip layout, style and paint for
   a section while it is off-screen, and do the work only when it scrolls
   close. On a long marketing page that is a real saving on first render.

   contain-intrinsic-size uses the `auto <length>` form: the length is a first
   guess for the scrollbar, and the browser replaces it with the real measured
   height once the section has been rendered once, so scrolling back up does
   not make the page jump.

   Deliberately not applied to anything that is an in-page anchor target with
   interactive state - the pricing tabs stay eagerly rendered.
   -------------------------------------------------------------------------- */
.defer-render {
  content-visibility: auto;
  contain-intrinsic-size: auto 40rem;
}

/* --------------------------------------------------------------------------
   11. Accessibility + motion
   -------------------------------------------------------------------------- */
.visually-hidden-focusable:not(:focus):not(:focus-within) {
  position: absolute !important; width: 1px; height: 1px; overflow: hidden; clip: rect(0,0,0,0);
}
.skip-link {
  position: absolute; left: .5rem; top: -3rem; z-index: 2000;
  background: var(--tj-ink); color: #fff;
  padding: .625rem 1rem; border-radius: var(--tj-radius-sm);
  transition: top .15s ease;
}
.skip-link:focus { top: .5rem; color: #fff; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

/* --------------------------------------------------------------------------
   12. Images
   --------------------------------------------------------------------------
   Channel avatars and post thumbnails come from Telegram and go stale, so a
   broken <img> is normal here rather than exceptional. Giving every image a
   ground and a fixed box keeps a dead one from collapsing the card around it
   and spraying alt text across the layout.
   -------------------------------------------------------------------------- */
img { max-width: 100%; height: auto; }
.img-frame {
  background: var(--tj-sunken);
  border-radius: var(--tj-radius-sm);
  overflow: hidden;
  display: block;
  position: relative;
}
.img-frame > img { width: 100%; height: 100%; object-fit: cover; display: block; }
.avatar-img, .channel-avatar {
  background: var(--tj-sunken);
  object-fit: cover;
}

/* --------------------------------------------------------------------------
   13. The long-form guide
   --------------------------------------------------------------------------
   The 5,000-word article at the foot of the homepage is stored in the content
   app with its own stylesheet, written before this design system existed. Its
   markup and its words are the owner's and are not touched here - only how it
   reads. This file loads after the block's own CSS, so equal-specificity
   rules win.

   What was wrong, in order of how much it cost the reader:

     * max-width: 100%. No reading measure at all, so on a wide monitor the
       lines ran the full container - around 150 characters. Anything past
       about 75 and the eye loses its place on the return sweep, which is the
       single most damaging thing you can do to a long article.
     * line-height: 2. Loose enough that lines stop reading as a paragraph and
       start reading as a list. 1.7-1.8 is where long-form sits.
     * A flat hierarchy: 16px body against an 18px h3 and a 22px h2. Three
       heading levels that are nearly the same size as the text under them
       cannot organise 31 sections.
     * line-height declared three times with three different values.
     * #32afed and #0088cc on white measure 2.5:1 and 3.3:1 - every heading
       and link in the article failed contrast.
   -------------------------------------------------------------------------- */
.tj-guide {
  /* ~68 characters at this size. The cards and grids inside are free to use
     the full width; it is the prose that needs the limit. */
  max-width: 52rem;
  margin-inline: auto;
  font-family: var(--bs-body-font-family);
  font-size: 1.0625rem;
  line-height: 1.75;
  color: var(--tj-body);
}

.tj-guide h1, .tj-guide h2, .tj-guide h3, .tj-guide h4 {
  font-family: "Plus Jakarta Sans", var(--bs-body-font-family);
  color: var(--tj-ink);
  letter-spacing: -.015em;
  text-wrap: balance;
}

/* A real scale, so the three levels are told apart at a glance rather than
   by reading them. */
.tj-guide .tj-header h1 { font-size: clamp(1.9rem, 1.4rem + 1.8vw, 2.5rem); line-height: 1.15; }
.tj-guide .tj-section h2 { font-size: 1.75rem; line-height: 1.25; }
.tj-guide .tj-section h3 { font-size: 1.25rem; line-height: 1.35; }
.tj-guide h4 { font-size: 1.0625rem; line-height: 1.4; }

/* Rhythm: a heading belongs to what follows it, so the space above is always
   larger than the space below. The old sheet spaced them evenly, which left
   every heading floating between two sections. */
.tj-guide .tj-section h2 { margin: 3.25rem 0 .85rem; }
.tj-guide .tj-section h3 { margin: 2.25rem 0 .6rem; }
.tj-guide .tj-section h2:first-child,
.tj-guide .tj-section h3:first-child { margin-top: 0; }

.tj-guide p { margin: 0 0 1.15rem; line-height: 1.75; }
.tj-guide p:last-child { margin-bottom: 0; }

.tj-guide ul, .tj-guide ol { margin: 0 0 1.25rem; padding-inline-start: 1.4rem; }
.tj-guide li { margin-bottom: .5rem; line-height: 1.7; }
.tj-guide li:last-child { margin-bottom: 0; }
.tj-guide strong { color: var(--tj-ink); font-weight: 600; }

/* The section container carried its own line-height of 1.5, contradicting
   the 2 on the wrapper. One value, set once. */
.tj-guide .tj-section p,
.tj-guide .tj-section li { line-height: 1.75; font-size: 1.0625rem; }

/* Colour. The article's blues were chosen against a white page and measured
   2.5:1 and 3.3:1; these are the palette's text tones, which pass. */
.tj-guide .tj-toc a,
.tj-guide .tj-section a,
.tj-guide a:not(.tj-btn) { color: var(--tg-blue-ink); }
.tj-guide .tj-toc a:hover,
.tj-guide .tj-section a:hover { color: var(--tj-ink); }
.tj-guide .tj-card h3,
.tj-guide .tj-toc h3,
.tj-guide .tj-info-box h4,
.tj-guide .tj-faq-question { color: var(--tj-ink); }
.tj-guide .tj-icon { color: var(--tg-blue-ink); }

/* Cards and notes sit inside the prose column, so they get the same measure
   and a little more room to breathe than the prose does. */
.tj-guide .tj-card,
.tj-guide .tj-note,
.tj-guide .tj-info-box,
.tj-guide .tj-faq-item { line-height: 1.7; }

.tj-guide .tj-faq-question { font-size: 1.0625rem; line-height: 1.45; }
.tj-guide .tj-faq-answer { font-size: 1rem; line-height: 1.75; }

.tj-guide .tj-blockquote {
  font-size: 1.125rem;
  line-height: 1.65;
  color: var(--tj-muted);
}

@media (max-width: 575.98px) {
  .tj-guide { font-size: 1rem; }
  .tj-guide .tj-section h2 { font-size: 1.5rem; margin-top: 2.5rem; }
  .tj-guide .tj-section h3 { font-size: 1.1875rem; margin-top: 1.75rem; }
}

/* One heading in the guide carries its colour as an inline style attribute,
   which no stylesheet rule can outrank. #e65100 measures 3.49:1 on the amber
   box behind it and the text is 18px, just under the size where 3:1 would be
   enough. !important is the only lever that reaches an inline style, and it
   is scoped to exactly this case rather than left loose. The content itself
   is untouched. */
.tj-guide [style*="#e65100"] { color: #c34500 !important; }   /* 4.63:1 */

/* Two corrections to the rules above, both caused by them.
   1. The section heading rhythm (2.25rem of air above an h3) also matched the
      h3 inside every card, which put a large dead gap between a card's icon
      and its title. A card is not a section; its heading starts at the top.
   2. The reading measure is for prose. The card grid needs three cards to fit
      across, and 52rem leaves room for two - so it broke to 2 + 1. Grids and
      tables step outside the measure; paragraphs stay inside it. */
.tj-guide .tj-card h3,
.tj-guide .tj-note h4,
.tj-guide .tj-info-box h4,
.tj-guide .tj-faq-item h3,
.tj-guide .tj-faq-item h4 { margin-top: 0; }

.tj-guide .tj-grid,
.tj-guide .tj-table,
.tj-guide .tj-timeline {
  width: calc(100% + 8rem);
  max-width: 100vw;
  margin-inline: -4rem;
}
@media (max-width: 991.98px) {
  .tj-guide .tj-grid,
  .tj-guide .tj-table,
  .tj-guide .tj-timeline { width: 100%; margin-inline: 0; }
}

/* The card row is a flexbox whose widths are calc(33.333% - 16px) plus 8px
   of margin each side - arithmetic that lands exactly on the container width
   and tips into a wrap, leaving the third card alone on its own row. A grid
   with auto-fit does the same job without the arithmetic, and fits as many
   cards per row as the width actually allows. */
.tj-guide .tj-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 1rem;
  margin-inline: -4rem;
}
.tj-guide .tj-grid > .tj-card { margin: 0; }
@media (max-width: 991.98px) {
  .tj-guide .tj-grid { margin-inline: 0; }
}

/* ==========================================================================
   Package icons and the slide-in cart
   --------------------------------------------------------------------------
   Every package type gets one colour and one glyph, used wherever a package
   appears on its way through the purchase - drawer, cart, checkout, order -
   so "the violet eye" means views at a glance on every screen.
   ========================================================================== */
.u-ico {
  flex: 0 0 auto;
  width: 2.75rem; height: 2.75rem;
  border-radius: .875rem;
  display: inline-flex; align-items: center; justify-content: center;
  color: #fff; font-size: 1.15rem;
  background: var(--u-bg, var(--tg-gradient));
  box-shadow: inset 0 -2px 0 rgba(0, 0, 0, .12), 0 6px 14px -6px var(--u-glow, rgba(34, 158, 217, .6));
}
.u-ico-sm { width: 2.25rem; height: 2.25rem; border-radius: .7rem; font-size: 1rem; }
.u-member   { --u-bg: linear-gradient(135deg, #2AABEE, #229ED9); --u-glow: rgba(34, 158, 217, .55); }
.u-view     { --u-bg: linear-gradient(135deg, #8b7cf6, #6C61DF); --u-glow: rgba(108, 97, 223, .55); }
.u-reaction { --u-bg: linear-gradient(135deg, #FF8A5B, #F0574A); --u-glow: rgba(240, 87, 74, .5); }
.u-other    { --u-bg: linear-gradient(135deg, #3fd6c2, #1fae9c); --u-glow: rgba(31, 174, 156, .5); }
.u-bot      { --u-bg: linear-gradient(135deg, #7d8aa3, #56627a); --u-glow: rgba(86, 98, 122, .45); }

.cart-drawer.offcanvas { width: min(100%, 25rem); border-left: 0 !important; outline: 0; box-shadow: -20px 0 60px rgba(10, 19, 40, .18); }
.cart-drawer .offcanvas-body { padding: 0; display: flex; flex-direction: column; }
.cd-head {
  display: flex; align-items: center; gap: .75rem;
  padding: 1.25rem 1.25rem 1rem;
  border-bottom: 1px solid var(--tj-line-soft);
}
.cd-head-mark {
  width: 2.25rem; height: 2.25rem; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--tj-good-wash); color: var(--tj-good); font-size: 1.1rem;
  animation: cd-pop .45s cubic-bezier(.2, 1.4, .4, 1) both;
}
@keyframes cd-pop { from { transform: scale(.3); opacity: 0; } to { transform: scale(1); opacity: 1; } }
.cd-head strong { display: block; font-size: 1rem; color: var(--tj-ink); }
.cd-head span { font-size: .8125rem; color: var(--tj-muted); }
.cd-head .btn-close { margin-inline-start: auto; }
.cd-items { list-style: none; margin: 0; padding: .5rem 1.25rem; flex: 1 1 auto; overflow-y: auto; }
.cd-items li { display: flex; align-items: center; gap: .75rem; padding: .75rem 0; border-bottom: 1px solid var(--tj-line-soft); }
.cd-items li:last-child { border-bottom: 0; }
.cd-items li.is-new { animation: cd-flash 1.4s ease; border-radius: .75rem; }
@keyframes cd-flash { 0%, 40% { background: var(--tj-brand-wash); } 100% { background: transparent; } }
.cd-name { flex: 1 1 auto; min-width: 0; }
.cd-name strong { display: block; font-size: .875rem; color: var(--tj-ink); }
.cd-name small { font-size: .75rem; color: var(--tj-muted); }
.cd-price { font-weight: 800; font-variant-numeric: tabular-nums; color: var(--tj-ink); }
.cd-foot { padding: 1rem 1.25rem calc(1.25rem + env(safe-area-inset-bottom)); border-top: 1px solid var(--tj-line); background: var(--tj-ground); }
.cd-total { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: .875rem; }
.cd-total span { color: var(--tj-muted); font-size: .875rem; }
.cd-total strong { font-size: 1.375rem; font-weight: 800; letter-spacing: -.02em; }
.cd-foot .btn { width: 100%; min-height: 3rem; font-weight: 700; }
.cd-foot .cd-more { display: flex; justify-content: space-between; margin-top: .75rem; font-size: .8125rem; }
.cd-foot .cd-more a { color: var(--tj-muted); text-decoration: none; }
.cd-foot .cd-more a:hover { color: var(--tj-ink); }
.cd-empty { padding: 3rem 1.5rem; text-align: center; color: var(--tj-muted); }

/* the cart badge nods when something lands in it */
.cart-badge.is-bump { animation: cd-bump .5s cubic-bezier(.2, 1.6, .4, 1); }
@keyframes cd-bump { 0% { transform: scale(1); } 40% { transform: scale(1.5); } 100% { transform: scale(1); } }

.svc-buy.is-busy { pointer-events: none; opacity: .8; }

@media (prefers-reduced-motion: reduce) {
  .cd-head-mark, .cd-items li.is-new, .cart-badge.is-bump { animation: none; }
}
