/* ob-globe.css : the rotating news globe of the Obsidian landing page.
   Companion of static/js/ob-globe.js, which builds every element below.
   Everything is scoped under .ob-globe, nothing leaks to the page.

   Layout : a square stage holding the canvas, and a floating news card that
   overlays the globe, on the right on desktop and at the bottom left on small
   screens. The landing hero may make the stage wider than the viewport in
   portrait : the script reads the visible strip back from the DOM.
   Colours are tuned for the navy hero (dark translucent surface, blue accent),
   the card itself mirrors the blog cards of /blog. */

.ob-globe {
  position: relative;
  width: 100%;
  max-width: 560px;
  margin-left: auto;
  margin-right: auto;
  /* Isolate the stacking context so the card never fights the hero content. */
  isolation: isolate;
}

.ob-globe-stage {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
}

/* Flat map variant : a 2 to 1 frame instead of the square of the sphere. The
   card moves to the south Pacific corner, the emptiest part of that frame, so
   Eurasia, Africa and the Americas all stay readable behind it. */
.ob-globe--flat .ob-globe-stage { aspect-ratio: 2 / 1; }

.ob-globe--flat .ob-globe-card {
  left: 0;
  right: auto;
  bottom: 6%;
  width: 260px;
}

@supports not (aspect-ratio: 1 / 1) {
  .ob-globe-stage { height: 0; padding-bottom: 100%; }
  .ob-globe--flat .ob-globe-stage { padding-bottom: 50%; }
}

.ob-globe-canvas {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  /* Vertical scrolling stays with the page, horizontal drags spin the globe. */
  touch-action: pan-y;
}

/* -------------------------------------------------------- cover background
   data-cover="1" : the sphere IS the page background. The layer is pinned to
   the viewport and the stage inside it is sized by the script (a square of
   max(vw, vh) * 1.15, centred, disc centre at 55 % of the viewport height), so
   the dotted Earth overflows the screen on every side. Nothing here takes the
   pointer except the news cards, which stay real links. */

.ob-globe--cover {
  position: fixed;
  inset: 0;
  width: auto;
  max-width: 100%;
  margin: 0;
  /* The stage inside is far wider than the screen ON PURPOSE : this clip is
     what keeps it from ever widening the page. Never move the stage with a
     negative margin on the page, it would escape this box. */
  overflow: hidden;
  pointer-events: none;
  z-index: 0;
}

.ob-globe--cover .ob-globe-stage {
  position: absolute;
  aspect-ratio: auto;
}

@supports not (aspect-ratio: 1 / 1) {
  .ob-globe--cover .ob-globe-stage { height: auto; padding-bottom: 0; }
}

.ob-globe--cover .ob-globe-card { pointer-events: auto; }

/* ---------------------------------------------------------------- the card */

.ob-globe-card {
  position: absolute;
  right: 0;
  bottom: 7%;
  z-index: 2;
  width: 280px;
  max-width: 72%;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid rgba(148, 163, 184, .14);
  border-radius: 14px;
  background: rgba(13, 22, 40, .78);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  box-shadow: 0 18px 44px rgba(2, 8, 23, .45);
  text-decoration: none;
  color: #e2e8f0;
  opacity: 0;
  transform: translateY(10px) scale(.985);
  transition: opacity .26s ease, transform .26s ease,
              border-color .25s ease, box-shadow .25s ease;
  will-change: opacity, transform;
}

.ob-globe-card[hidden] { display: none; }

.ob-globe-card.is-visible {
  opacity: 1;
  transform: translateY(0) scale(1);
}

.ob-globe-card:hover,
.ob-globe-card:focus-visible {
  border-color: rgba(96, 165, 250, .42);
  box-shadow: 0 18px 44px rgba(2, 8, 23, .55), 0 0 0 1px rgba(96, 165, 250, .18);
}

.ob-globe-card:focus-visible {
  outline: 2px solid rgba(125, 211, 252, .75);
  outline-offset: 2px;
}

.ob-globe-card-media {
  display: block;
  width: 100%;
  aspect-ratio: 600 / 340;
  overflow: hidden;
  background: rgba(30, 41, 59, .7);
  flex-shrink: 0;
}

.ob-globe-card-media[hidden] { display: none; }

@supports not (aspect-ratio: 1 / 1) {
  .ob-globe-card-media { height: 150px; }
}

.ob-globe-card-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.ob-globe-card-body {
  display: flex;
  flex-direction: column;
  gap: 7px;
  padding: 12px 14px 13px;
}

.ob-globe-card-category {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .8px;
  color: #7db2ff;
}

.ob-globe-card-title {
  font-size: 14px;
  font-weight: 600;
  line-height: 1.32;
  color: #f1f5f9;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.ob-globe-card-flag {
  display: inline-block;
  width: 1.2em;
  height: .9em;
  border-radius: 2px;
  margin-right: 7px;
  vertical-align: -.08em;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .14);
  background-size: cover;
  background-position: 50%;
}

.ob-globe-card-flag[hidden] { display: none; }

.ob-globe-card-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-top: 3px;
  padding-top: 9px;
  border-top: 1px solid rgba(148, 163, 184, .16);
  font-size: 11px;
  color: rgba(203, 213, 225, .66);
}

.ob-globe-card-reading {
  white-space: nowrap;
  flex-shrink: 0;
}

/* ---------------------------------------------------------------- light theme
   data-theme="light" : the same component on a light page. The canvas colours
   are decided in the script (accent blue land dots at a background alpha,
   accent pins with a white halo) ; here the CARDS become the /blog card : a
   white surface, a 1px border, a 14px radius, dark text, an accent eyebrow
   and a faint date.

   The class sits on the CARD itself (ob-globe.js writes it at build time),
   never on an ancestor : a carousel card is mounted in the PAGE's rail,
   outside this component, so an ancestor scoped rule would miss it. */

.ob-globe-card--light {
  background: var(--color-surface, #fff);
  border: 1px solid var(--color-border, rgba(0, 0, 0, .08));
  border-radius: 14px;
  color: var(--color-text-primary, #101828);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  box-shadow: 0 1px 2px rgba(16, 24, 40, .04), 0 12px 32px -16px rgba(16, 24, 40, .22);
}

.ob-globe-card--light:hover,
.ob-globe-card--light:focus-visible {
  border-color: rgba(43, 122, 228, .35);
  box-shadow: 0 1px 2px rgba(16, 24, 40, .05), 0 16px 36px -16px rgba(43, 122, 228, .35);
}

.ob-globe-card--light .ob-globe-card-media { background: var(--color-bg, #f5f6f8); }
.ob-globe-card--light .ob-globe-card-category { color: var(--color-accent, #2b7ae4); }
.ob-globe-card--light .ob-globe-card-title { color: var(--color-text-primary, #101828); }
.ob-globe-card--light .ob-globe-card-flag { box-shadow: 0 0 0 1px rgba(16, 24, 40, .12); }

.ob-globe-card--light .ob-globe-card-meta {
  border-top-color: var(--color-border, rgba(0, 0, 0, .08));
  color: var(--color-text-faint, #7a818c);
}

/* Decoration only (data-cards-static="1") : the card is a <div>, it takes no
   pointer, no focus and no hover, so nothing on a form page competes with the
   field. The landing hero keeps its real links. */

.ob-globe-card--static {
  pointer-events: none;
  cursor: default;
  -webkit-user-select: none;
  user-select: none;
}

.ob-globe-card--static:hover {
  border-color: var(--color-border, rgba(0, 0, 0, .08));
  box-shadow: 0 1px 2px rgba(16, 24, 40, .04), 0 12px 32px -16px rgba(16, 24, 40, .22);
}

/* ------------------------------------------------------------- the row card
   data-card-layout="row" : a 96x72 thumbnail on the left, the text on the
   right. The onboarding pages use it (a row reads faster beside a form than a
   magazine card) ; the landing keeps the default column card. */

.ob-globe-card--row {
  flex-direction: row;
  align-items: stretch;
  gap: 0;
  padding: 0;
  width: 320px;
  max-width: 100%;
  min-height: 88px;
  /* The thumbnail is FLUSH : it bleeds to the card's own edges, so the card
     is the only thing that rounds and the only thing that clips. */
  overflow: hidden;
}

.ob-globe-card--row .ob-globe-card-media {
  flex: 0 0 104px;
  width: 104px;
  height: auto;
  aspect-ratio: auto;
  align-self: stretch;
  /* The card's radius, on the left corners only : no inner radius, no
     border, no padding between the image and the card edge. */
  border-radius: inherit;
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}

.ob-globe-card--row .ob-globe-card-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.ob-globe-card--row .ob-globe-card-body {
  flex: 1 1 auto;
  min-width: 0;
  padding: 10px 12px;
  gap: 4px;
  justify-content: center;
}

.ob-globe-card--row .ob-globe-card-title {
  -webkit-line-clamp: 2;
  line-clamp: 2;
  font-size: 13px;
}

.ob-globe-card--row .ob-globe-card-meta {
  margin-top: 2px;
  padding-top: 6px;
}

/* ------------------------------------------------------- the rolling window
   data-cards="N" : N cards live at once, each in a fixed slot, and the script
   only swaps which news each slot carries. Desktop keeps them in a right hand
   column (top, middle, bottom) so the callout lines fan across the sphere. */

.ob-globe--multi .ob-globe-card {
  width: 260px;
  max-width: 30vw;
  max-height: 30vh;
}

.ob-globe--multi .ob-globe-card--row { width: 320px; max-width: 34vw; }

.ob-globe--multi .ob-globe-card--s0,
.ob-globe--multi .ob-globe-card--s1,
.ob-globe--multi .ob-globe-card--s2 {
  right: 5%;
  left: auto;
  bottom: auto;
}

.ob-globe--multi .ob-globe-card--s0 { top: 12%; }
.ob-globe--multi .ob-globe-card--s1 { top: 42%; }
.ob-globe--multi .ob-globe-card--s2 { top: 72%; }

/* A short window cannot hold three illustrated cards : drop the images and the
   meta line rather than let the bottom card run off the screen. */
@media (max-height: 820px) {
  .ob-globe--multi .ob-globe-card-media { display: none; }
  .ob-globe--multi .ob-globe-card-meta { display: none; }
  /* A 72px thumbnail costs no height in a row card : it stays. */
  .ob-globe--multi .ob-globe-card--row .ob-globe-card-media { display: block; }
}

/* --------------------------------------------------------- free placement
   ob-globe.js PLACE : on a cover with several cards (desktop), the component
   sets each card in the free space NEAR ITS OWN PIN, with inline left, top
   and width. The slot geometry above stops applying to such a card ; the
   same fade and scale in on appearance. A card the component could not place
   keeps its slot. */

.ob-globe--free .ob-globe-card--placed {
  right: auto;
  bottom: auto;
  max-width: none;
  max-height: none;
}

/* --------------------------------------------------------------- the rail
   data-rail="#sel" : under 900 px the cards are NOT slots floating over the
   sphere any more (they covered the form on a phone). The script mounts them
   inside the page's own rail, right under its card, as a horizontal
   snap-scroll row with no callout line. The rail element belongs to the page,
   so these rules must undo the absolute positioning of a floating card. */

.ob-globe-rail {
  display: none;
  gap: 12px;
  margin: 16px auto 0;
  /* 24px of padding on each side IS the peek : the card is 100 % of the
     content box, so calc(100% - 48px) of the rail, and the neighbours show
     through the padding on the left and on the right. */
  padding: 2px 24px 6px;
  scroll-padding-inline: 24px;
  width: 100%;
  max-width: 100%;
  min-width: 0;
  box-sizing: border-box;
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
}

.ob-globe-rail::-webkit-scrollbar { display: none; }

@media (max-width: 899px) {
  .ob-globe-rail { display: flex; }
}

/* A carousel card is a normal flex item again : every trace of the floating
   slot has to be undone, the slot rules live on an ancestor this card does
   not have. */
.ob-globe-rail .ob-globe-card {
  position: relative;
  inset: auto;
  top: auto;
  right: auto;
  bottom: auto;
  left: auto;
  flex: 0 0 100%;
  width: auto;
  max-width: 100%;
  max-height: none;
  scroll-snap-align: center;
}

/* Portrait phone, NO rail given : the old row of three compact cards over the
   sphere stays the fallback. Scoped to a COVER globe : those vw offsets are
   only safe inside the fixed, clipped root, never on an in-flow one. */
@media (max-width: 900px) and (orientation: portrait) {
  .ob-globe--cover.ob-globe--multi .ob-globe-card--s0,
  .ob-globe--cover.ob-globe--multi .ob-globe-card--s1,
  .ob-globe--cover.ob-globe--multi .ob-globe-card--s2 {
    top: auto;
    right: auto;
    bottom: 12px;
    width: 30vw;
    max-width: none;
    max-height: 24vh;
  }
  .ob-globe--cover.ob-globe--multi .ob-globe-card--s0 { left: 2vw; }
  .ob-globe--cover.ob-globe--multi .ob-globe-card--s1 { left: 35vw; }
  .ob-globe--cover.ob-globe--multi .ob-globe-card--s2 { left: 68vw; }

  .ob-globe--multi .ob-globe-card-media,
  .ob-globe--multi .ob-globe-card-meta { display: none; }
  .ob-globe--cover.ob-globe--multi .ob-globe-card--row .ob-globe-card-media { display: block; }
  .ob-globe--multi .ob-globe-card-body { padding: 8px 9px 9px; gap: 4px; }
  .ob-globe--multi .ob-globe-card-category { font-size: 9.5px; letter-spacing: .5px; }
  .ob-globe--multi .ob-globe-card-title {
    font-size: 11.5px;
    -webkit-line-clamp: 4;
    line-clamp: 4;
  }
}

/* ------------------------------------------------------------- small screens
   The card keeps floating OVER the globe, moved to the bottom left corner so
   the callout line has the width of the sphere to cross. */

@media (max-width: 640px) {
  .ob-globe { max-width: 340px; }

  .ob-globe-card {
    left: 12px;
    right: auto;
    bottom: 12px;
    width: min(300px, 70vw);
    max-width: none;
  }
}

@media (max-width: 380px) {
  .ob-globe { max-width: 300px; }
}

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

@media (prefers-reduced-motion: reduce) {
  .ob-globe-card {
    transition: opacity .2s linear;
    transform: none;
  }
  .ob-globe-card.is-visible { transform: none; }
}
