/* -----------------------------------------------------------------------------
   Author: bitsEverywhere DOO
   ----------------------------------------------------------------------------- */
/* eFTI Connect additive layer over the Soft template — reveal animations + brand tweaks */

[data-reveal] {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity .7s cubic-bezier(.22, .61, .36, 1), transform .7s cubic-bezier(.22, .61, .36, 1);
  will-change: opacity, transform;
}
[data-reveal].reveal-visible { opacity: 1; transform: none; }
[data-reveal="2"] { transition-delay: .12s; }
[data-reveal="3"] { transition-delay: .24s; }
[data-reveal="4"] { transition-delay: .36s; }

@media (prefers-reduced-motion: reduce) {
  [data-reveal] { opacity: 1; transform: none; transition: none; }
}

/* JS unavailable → never hide content (SEO + no-JS safety) */
.no-js [data-reveal] { opacity: 1; transform: none; }

.dropdown-menu .dropdown-item.active { font-weight: 700; }

/* No-JS contact-form fallback: server redirects to #sent / #sent-error;
   only the matching anchor target is shown (see lib/contact.js). */
.cf-note { display: none; }
.cf-note:target { display: block; }

/* Footer in dark Microsoft-Teams violet (matches primary #5b5fc7); the
   contact page's bg-dark strip merges into the footer, so it follows. */
footer.bg-dark,
.bg-dark.min-h-150px {
  background-color: #383966 !important;
}

/* brand slogan (kept in English across locales, like in the app header) */
.brand-tagline { letter-spacing: .14em; }

/* Billing toggle: the section owns the mode, CSS does the hiding.
   Scoped to html:not(.no-js) so that without JS both variants stay visible and
   the toggle is hidden instead of sitting there doing nothing. Deliberately not
   the [hidden] attribute — style.css declares [hidden]{display:none!important},
   which no .no-js rule could override. */
html:not(.no-js) [data-billing-mode="monthly"] .plan-price[data-billing="yearly"],
html:not(.no-js) [data-billing-mode="yearly"] .plan-price[data-billing="monthly"] {
  display: none;
}
.no-js [data-billing-toggle] { display: none; }

/* Launch-offer heading. The two-line split itself is structural — the heading is
   authored as two sentences and each renders as its own block (see the offer card
   in templates/_macros.njk), so it does not depend on this rule. `balance` only
   governs what happens when one of those sentences has to wrap again on a narrow
   viewport: it evens that wrap instead of leaving one word stranded. Browsers
   without support (pre-2023) fall back to ordinary wrapping. */
[data-launch-offer] .card-header h3 {
  text-wrap: balance;
}

/* Ratification-table status marks: green tick = ratified, amber clock = in
   progress, red cross = not yet. Semantic colours rather than brand violet,
   because the marks mean done / waiting / no, not "eFTI Connect".

   None of the three is the theme's own token: --bs-success (#2dca8c),
   --bs-warning (#ff9f1c) and --bs-danger (#ff5c75) reach only 2.0-2.8:1 against
   these rows, under the 3:1 WCAG 1.4.11 floor for a meaningful graphic and
   simply hard to see — which would defeat the emphasis. Each keeps its theme
   hue, darkened until it clears that floor on the striped rows.

   The pending mark sits at hue 45deg — yellow, not the orange it was — and that
   costs contrast: yellow is the lightest hue there is, so on a white row it can
   only stay visible as a dark gold. 3.16:1 is the yellowest reading that still
   clears the floor; anything brighter drops to ~2:1 and starts to disappear. A
   genuinely bright yellow would need a dark chip behind it, not a bare glyph.

   Colour is reinforcement only: the marks are aria-hidden and every cell states
   its status in words, so nothing is lost when the colours cannot be seen or
   told apart. */
.icon-ratified { color: #118557; }  /* 4.41:1 — theme mint, darkened */
.icon-pending  { color: #b28600; }  /* 3.16:1 — gold at hue 45deg (see above) */
.icon-not-yet  { color: #d3323f; }  /* 4.64:1 — theme red, darkened */

/* Hero screenshot carousel.

   The vendored Bootstrap is 5.0.0-beta1, which still expects the OLD carousel
   markup: <a> for the controls and <li> for the indicators. The template uses
   the modern <button> for both, because a control that moves a slide is not a
   link and should not be one — but that means neither element picks up the
   theme's styling, and both arrive wearing the browser's default button chrome
   (a grey face over 15% of the image on each side). The resets below supply
   what the vendored stylesheet does not. Drop them only if Bootstrap is
   upgraded past beta1, and check the controls again if you do.

   Two further departures from the defaults, because the slides are light app
   screenshots rather than photos:
   - the control icons ship as white SVGs, invisible against a white UI, so they
     get a dark brand-tinted disc behind them;
   - the indicators are absolutely positioned over the foot of the slide, on top
     of the screenshot's own content, so they move out below the image. */
#app-screens .carousel-control-prev,
#app-screens .carousel-control-next {
  width: 10%;
  padding: 0;
  border: 0;
  background: none;
}
#app-screens .carousel-control-prev-icon,
#app-screens .carousel-control-next-icon {
  background-color: rgba(56, 57, 102, .75);
  border-radius: 50%;
  padding: 1.15rem;
  background-size: 45%;
}
#app-screens .carousel-indicators {
  position: static;
  margin: .75rem 0 0;
}
#app-screens .carousel-indicators [data-bs-target] {
  box-sizing: border-box;
  flex: 0 0 auto;
  width: 10px;
  height: 10px;
  margin: 0 4px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background-color: #383966;
  opacity: .3;
  cursor: pointer;
}
#app-screens .carousel-indicators .active { opacity: 1; }

/* Hero screenshot wrapper. Sits outside .container so the slide width is set
   here rather than by the 1320px content container, and capped below the
   screenshots' intrinsic 1866x1305 — at full size the hero runs taller than the
   fold and pushes the copy below it off screen. One number controls it: raise
   max-width toward 1866px for a bigger, sharper hero, lower it for a shorter
   one. Height follows the slides' shared aspect ratio, which the test pins. */
.hero-screens {
  max-width: 1280px;
  margin-left: auto;
  margin-right: auto;
  padding-left: 1rem;
  padding-right: 1rem;
}

/* Carousel stage. Bootstrap shows a slide with display:block, which lets a
   differently-shaped image resize the whole hero as it comes round; these rules
   turn each visible slide into a centring box that fills the fixed-ratio stage
   (set inline from slide 1) and contain the image inside it. A portrait phone
   screenshot among landscape desktop ones is then letterboxed, and the hero
   never changes height. Only the visible slides get display:flex — applying it
   to .carousel-item itself would override Bootstrap's display:none and show
   every slide at once. */
#app-screens .carousel-item.active,
#app-screens .carousel-item-next,
#app-screens .carousel-item-prev {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
}
#app-screens .carousel-item img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
}


/* ---------------------------------------------------------------------------
   Blog article body.
   The body is rendered from Markdown (lib/blog.js), so it carries semantic
   h2/h3/ul/blockquote rather than the hand-written <h5> of the template's
   blog-single.html. These rules give those elements the same rhythm the
   template's article had, scoped to .blog-article so nothing else moves.
   --------------------------------------------------------------------------- */
.blog-article > h2 {
  font-size: 1.5rem;
  font-weight: 700;
  margin-top: 2.5rem;
  margin-bottom: 1rem;
}
.blog-article > h3 {
  font-size: 1.2rem;
  font-weight: 600;
  margin-top: 2rem;
  margin-bottom: .75rem;
}
.blog-article > h2:first-of-type { margin-top: 1.5rem; }
.blog-article > p { margin-bottom: 1.25rem; }
.blog-article > p.lead { margin-bottom: 1.75rem; }
.blog-article > ul,
.blog-article > ol { margin-bottom: 1.5rem; padding-left: 1.25rem; }
.blog-article > ul > li,
.blog-article > ol > li { margin-bottom: .5rem; }
.blog-article blockquote {
  border-left: 3px solid var(--bs-primary, #4c50e6);
  padding-left: 1.25rem;
  margin: 1.75rem 0;
  font-size: 1.05rem;
}
.blog-article figure { margin: 1.75rem 0; }
.blog-article figcaption { font-size: .875rem; color: #64748b; text-align: center; margin-top: .5rem; }
.blog-article img { max-width: 100%; height: auto; }
.blog-article hr { margin: 2.5rem 0; }

/* Post hero. The image arrives as the --blog-hero custom property (set inline
   per post), not as background-image, so the phone rule below can swap in the
   post's portrait variant. Deliberately NOT a jarallax parallax section: see
   templates/blog-post.njk. */
.blog-hero {
  /* #1a1a1a is the artwork's own canvas colour, so the band either side of the
     frame is indistinguishable from it. */
  background-color: #1a1a1a;
  background-image: var(--blog-hero);
  /* Fit the WHOLE frame above the article card instead of cropping to fill:
     the card is pulled up over the hero by .mt-n10 (-5rem), so the artwork is
     scaled to the height that clears it. A cover crop slices whatever the
     artwork keeps near its edges — on the brand hero, the tagline. */
  background-size: auto calc(100% - 5rem);
  background-position: center top;
  background-repeat: no-repeat;
}

/* A post that ships its own photograph instead of the brand artwork. Nothing
   near its edges has to survive, and a fitted photo letterboxes against the
   #1a1a1a band as if the image had failed to load — so it fills the hero. */
.blog-hero-photo {
  background-size: cover;
  background-position: center;
}

/* Phone hero. The landscape frame crops to a narrow window here, so a post may
   ship a portrait companion whose lockup sits at the top, leaving clear canvas
   for the title; --blog-hero is the fallback when it has none, shifted to 8%
   so a centred crop does not miss the brand column in the artwork's left third.
   Anchored to the top: the portrait variant is composed for that. */
@media (max-width: 767.98px) {
  .blog-hero {
    background-image: var(--blog-hero-portrait, var(--blog-hero));
    /* The portrait variant is composed for this band, so it fills it. */
    background-size: cover;
    /* A post with a portrait variant sets its own position (that artwork is
       composed centred, anchored top); the 8% fallback only exists to drag the
       landscape artwork's left-third column into this narrow crop. */
    background-position: var(--blog-hero-phone-position, 8% center);
  }
}

/* ---------------------------------------------------------------------------
   Page background — the product's doodle pattern.

   Outline-only logistics motifs (truck, parcel, eCMR document, QR code, pallet,
   pin, signature pen, clock) on a 320px tile, exactly as the app draws them:
   the value below is the output of doodleBackgroundImage() from
   ClientApp/packages/shared/src/theme/doodle-pattern.ts, so the marketing site
   and the product share one surface rather than two lookalikes.

   Regenerate after changing that module:
     node --input-type=module -e "const m = await import('<repo>/src/eFTIConnect/Web/eFTIConnect.Web/ClientApp/packages/shared/src/theme/doodle-pattern.ts'); console.log(m.doodleBackgroundImage())"

   Inlined rather than linked as its own .svg on purpose: /static is served
   immutable for a year, and only a stylesheet URL carries the ?v= hash, so a
   linked tile could never be updated for a returning visitor.
   --------------------------------------------------------------------------- */
:root {
  --doodle-pattern: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22320%22%20height%3D%22320%22%20viewBox%3D%220%200%20320%20320%22%3E%3Cg%20fill%3D%22none%22%20stroke%3D%22rgba(127%2C%20127%2C%20127%2C%200.16)%22%20stroke-width%3D%221.6%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cg%20transform%3D%22translate(16%2022)%20rotate(-6)%22%3E%3Crect%20x%3D%222%22%20y%3D%224%22%20width%3D%2224%22%20height%3D%2216%22%20rx%3D%221.5%22%2F%3E%3Cpath%20d%3D%22M26%209h8l6%206v5H26z%22%2F%3E%3Ccircle%20cx%3D%2210%22%20cy%3D%2223%22%20r%3D%223%22%2F%3E%3Ccircle%20cx%3D%2233%22%20cy%3D%2223%22%20r%3D%223%22%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%22translate(216%2016)%20rotate(8)%22%3E%3Crect%20x%3D%221%22%20y%3D%221%22%20width%3D%2224%22%20height%3D%2224%22%20rx%3D%222%22%2F%3E%3Crect%20x%3D%225%22%20y%3D%225%22%20width%3D%226%22%20height%3D%226%22%2F%3E%3Crect%20x%3D%2215%22%20y%3D%225%22%20width%3D%226%22%20height%3D%226%22%2F%3E%3Crect%20x%3D%225%22%20y%3D%2215%22%20width%3D%226%22%20height%3D%226%22%2F%3E%3Cpath%20d%3D%22M15%2015h2M19%2017h2M15%2019v2%22%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%22translate(122%2066)%20rotate(-10)%22%3E%3Cpath%20d%3D%22M2%202h14l8%208v20H2z%22%2F%3E%3Cpath%20d%3D%22M16%202v8h8%22%2F%3E%3Cpath%20d%3D%22M6%2016h14M6%2021h14M6%2026h9%22%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%22translate(258%20122)%20rotate(6)%22%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%2210%22%2F%3E%3Cpath%20d%3D%22M12%206v6l4%203%22%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%22translate(30%20142)%20rotate(7)%22%3E%3Crect%20x%3D%222%22%20y%3D%228%22%20width%3D%2226%22%20height%3D%2220%22%20rx%3D%221.5%22%2F%3E%3Cpath%20d%3D%22M2%208l13-6%2013%206M15%208v20%22%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%22translate(152%20152)%20rotate(-6)%22%3E%3Cpath%20d%3D%22M10%2026C4%2018%202%2014%202%209a8%208%200%201%201%2016%200c0%205-2%209-8%2017z%22%2F%3E%3Ccircle%20cx%3D%2210%22%20cy%3D%229%22%20r%3D%223%22%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%22translate(230%20212)%20rotate(-14)%22%3E%3Cpath%20d%3D%22M4%2026L22%208l4-4%202%202-4%204L6%2028z%22%2F%3E%3Cpath%20d%3D%22M2%2030h8%22%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%22translate(58%20238)%20rotate(4)%22%3E%3Cpath%20d%3D%22M2%202h32M2%2012h32M8%202v10M18%202v10M28%202v10%22%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%22translate(158%20262)%20rotate(9)%20scale(0.75)%22%3E%3Crect%20x%3D%222%22%20y%3D%228%22%20width%3D%2226%22%20height%3D%2220%22%20rx%3D%221.5%22%2F%3E%3Cpath%20d%3D%22M2%208l13-6%2013%206M15%208v20%22%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%22translate(288%20286)%20rotate(10)%20scale(0.7)%22%3E%3Cpath%20d%3D%22M10%2026C4%2018%202%2014%202%209a8%208%200%201%201%2016%200c0%205-2%209-8%2017z%22%2F%3E%3Ccircle%20cx%3D%2210%22%20cy%3D%229%22%20r%3D%223%22%2F%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E");
}

body {
  background-color: #fff;
  background-image: var(--doodle-pattern);
  background-repeat: repeat;
}

/* The home hero (.bg-gray-100, #f7f7ff) deliberately stays a plain tint — the
   pattern was tried there and dropped. Its !important colour already blocks the
   body pattern, so nothing more is needed to keep it clean. */
