diff --git a/website/components/basic-hero/index.jsx b/website/components/basic-hero/index.jsx new file mode 100644 index 0000000000..4c48ca19ce --- /dev/null +++ b/website/components/basic-hero/index.jsx @@ -0,0 +1,41 @@ +import Button from '@hashicorp/react-button' + +export default function BasicHero({ + heading, + content, + links, + brand, + backgroundImage, +}) { + console.log('background?', backgroundImage) + return ( +
+
+

{heading}

+ {content &&

{content}

} + {links && links.length > 0 && ( +
+ {links.map((link, stableIdx) => { + const buttonVariant = stableIdx === 0 ? 'primary' : 'secondary' + const linkType = link.type || 'inbound' + return ( +
+ )} +
+
+ ) +} diff --git a/website/components/basic-hero/style.css b/website/components/basic-hero/style.css new file mode 100644 index 0000000000..247b5ec189 --- /dev/null +++ b/website/components/basic-hero/style.css @@ -0,0 +1,63 @@ +.g-basic-hero { + padding: 88px 0; + + & .g-type-display-1 { + color: var(--gray-1); + text-align: center; + margin-left: auto; + margin-right: auto; + margin-top: 0; + max-width: 14em; + } + + & .g-type-body-large { + color: var(--gray-3); + margin: 0 auto 0 auto; + text-align: center; + max-width: 40em; + } + + & .links { + display: flex; + flex-wrap: wrap; + justify-content: center; + /* + * Margins here compensate for extra 8px margin on buttons + * which are needed to center and space properly regardless of whether + * buttons are wrapping to multiple lines or not. + */ + margin-top: calc(32px - 8px); + margin-bottom: -8px; + @media (--large) { + margin-top: calc(40px - 8px); + } + + & .g-btn { + /* + * This ensures 16px between buttons at all times, while maintaining proper centering + * when buttons wrap to multiple lines. + * There will be an extra 8px space on all sides of the button group. + * The top and bottom are accounted for by the -8px adjustment on `.action` margins. + * The left and right excess is left as is - it's needed for proper centering when wrapping. + */ + margin: 8px; + } + } + + &.has-background { + background-repeat: no-repeat; + background-color: var(--gray-7); + background-image: url(/img/hero-pattern.svg); + width: 100%; + background-size: cover; + background-position: center; + + @media (max-width: 800px) { + background-image: url(/img/hero-pattern-mobile.svg); + } + + & .g-btn { + background: var(--gray-7); + } + } +} diff --git a/website/components/consul-enterprise-comparison/index.jsx b/website/components/consul-enterprise-comparison/index.jsx new file mode 100644 index 0000000000..ff705678d0 --- /dev/null +++ b/website/components/consul-enterprise-comparison/index.jsx @@ -0,0 +1,40 @@ +import EnterpriseComparison from '../enterprise-comparison' + +const technicalComplexity = { + title: 'Technical Complexity', + label: 'Open Source', + imageUrl: + 'https://www.datocms-assets.com/2885/1579883486-complexity-basic.png', + description: + 'Consul open source enables individuals to discover services and securely manage connections between them across cloud, on-premise, and hybrid environments.', + link: { + text: 'View Open Source Features', + url: 'https://www.hashicorp.com/products/consul/pricing/', + type: 'outbound', + }, +} + +const organizationalComplexity = { + title: 'Organizational Complexity', + label: 'Enterprise', + imageUrl: + 'https://www.datocms-assets.com/2885/1579883488-complexity-advanced.png', + description: + 'Consul Enterprise provides the foundation for organizations to build and govern an enterprise-ready service networking environment for multiple teams across multiple clouds', + link: { + text: 'View Enterprise Features', + url: 'https://www.hashicorp.com/products/consul/pricing/', + type: 'outbound', + }, +} + +export default function NomadEnterpriseInfo() { + return ( + + ) +} diff --git a/website/components/consul-enterprise-comparison/style.css b/website/components/consul-enterprise-comparison/style.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/website/components/enterprise-comparison/img/arrow.svg b/website/components/enterprise-comparison/img/arrow.svg new file mode 100644 index 0000000000..2dcd4406c9 --- /dev/null +++ b/website/components/enterprise-comparison/img/arrow.svg @@ -0,0 +1,4 @@ + + + + diff --git a/website/components/enterprise-comparison/img/complexity-advanced.png b/website/components/enterprise-comparison/img/complexity-advanced.png new file mode 100644 index 0000000000..eeac1f3b96 Binary files /dev/null and b/website/components/enterprise-comparison/img/complexity-advanced.png differ diff --git a/website/components/enterprise-comparison/img/complexity-basic.png b/website/components/enterprise-comparison/img/complexity-basic.png new file mode 100644 index 0000000000..9a35d60b14 Binary files /dev/null and b/website/components/enterprise-comparison/img/complexity-basic.png differ diff --git a/website/components/enterprise-comparison/index.jsx b/website/components/enterprise-comparison/index.jsx new file mode 100644 index 0000000000..1b8784e398 --- /dev/null +++ b/website/components/enterprise-comparison/index.jsx @@ -0,0 +1,47 @@ +import Image from '@hashicorp/react-image' +import Button from '@hashicorp/react-button' +import InlineSvg from '@hashicorp/react-inline-svg' +import ArrowIcon from './img/arrow.svg?include' + +export default function EnterpriseInfo({ title, itemOne, itemTwo, brand }) { + return ( +
+
+

{title}

+ +
+
+ +
{itemOne.label}
+

{itemOne.title}

+ +

{itemOne.description}

+
+
+
+ +
+
+ +
{itemTwo.label}
+

{itemTwo.title}

+ +

{itemTwo.description}

+
+
+
+
+ ) +} diff --git a/website/components/enterprise-comparison/style.css b/website/components/enterprise-comparison/style.css new file mode 100644 index 0000000000..1b4b72989d --- /dev/null +++ b/website/components/enterprise-comparison/style.css @@ -0,0 +1,92 @@ +.g-enterprise-comparison { + padding-top: 128px; + padding-bottom: 128px; + background: var(--gray-7); + + & h2 { + text-align: center; + } + + @media (max-width: 800px) { + padding-top: 64px; + padding-bottom: 64px; + } + + & .content-container { + display: flex; + justify-content: space-between; + margin: 0 auto 64px auto; + + @media (max-width: 800px) { + flex-wrap: wrap; + } + & .item { + flex-basis: 50%; + justify-content: center; + text-align: center; + margin-top: 64px; + + @media (max-width: 800px) { + margin-top: 64px; + flex-basis: 100%; + } + + & .g-type-label-strong { + margin-top: 64px; + + @media (max-width: 800px) { + margin-top: 32px; + } + } + + & h4 { + white-space: pre; + margin-top: 24px; + margin-bottom: 8px; + + @media (max-width: 800px) { + margin-top: 16px; + } + } + + & picture { + display: inline-block; + } + + & img { + max-width: 160px; + max-height: 98px; + } + & p { + margin-top: 0; + margin-bottom: 24px; + + @media (max-width: 800px) { + max-width: 600px; + margin-right: auto; + margin-left: auto; + } + } + } + + & .spacer { + & .vertical-spacer { + height: 93px; + } + + & .arrow { + display: flex; + align-items: center; + } + + @media (max-width: 800px) { + display: none; + } + } + } + + & .more-features-link { + display: flex; + justify-content: center; + } +} diff --git a/website/components/learn-callout/index.jsx b/website/components/learn-callout/index.jsx new file mode 100644 index 0000000000..c76c61d47b --- /dev/null +++ b/website/components/learn-callout/index.jsx @@ -0,0 +1,57 @@ +import Button from '@hashicorp/react-button' + +export default function LearnNomad({ headline, brand, items }) { + return ( +
+
+
+ {/* need this wrapper to flex center the .column-content */} +
+
+

{headline}

+
+
+ {items.map((item) => { + return ( + +
+
+
{item.time}
+ {item.title} +
+
+
+ +

{item.title}

+
+
+
+
+ ) + })} +
+
+
+ ) +} diff --git a/website/components/learn-callout/style.css b/website/components/learn-callout/style.css new file mode 100644 index 0000000000..62be0028ce --- /dev/null +++ b/website/components/learn-callout/style.css @@ -0,0 +1,174 @@ +.g-learn-callout { + padding-top: 88px; + padding-bottom: 88px; + background-image: url(/img/nomad-panel-learn.svg); + background-size: contain; + background-position: bottom right; + background-repeat: no-repeat; + + @media (max-width: 768px) { + padding-top: 64px; + padding-bottom: 64px; + } + + & .learn-container { + @media (max-width: 1200px) { + display: flex; + flex-wrap: wrap; + justify-content: center; + } + & .mobile-button { + @media (min-width: 1201px) { + display: none; + } + } + } + + & .column-container { + display: flex; + justify-content: space-between; + margin: 0 36px; + + @media (max-width: 1200px) { + margin: 0 -16px; + flex-wrap: wrap; + } + + & .column-content { + & h2 { + @media (max-width: 1200px) { + margin: 0; + } + } + } + + & .desktop-button { + @media (max-width: 1200px) { + display: none; + } + } + + & > div { + display: flex; + flex-wrap: wrap; + align-items: center; + margin: 0 16px; + width: 33.333%; + overflow: auto; + + @media (max-width: 1200px) { + text-align: center; + display: block; + width: 100%; + margin: 0; + margin-bottom: 48px; + } + } + + & > a { + margin: 0 16px; + width: 33.333%; + transition: box-shadow 0.25s, transform 0.25s, -webkit-transform 0.25s; + + &:hover { + box-shadow: 0px 16px 28px rgba(37, 38, 45, 0.12); + transform: translateY(-4px); + } + + @media (max-width: 1200px) { + width: calc(50% - 32px); + margin-bottom: 48px; + } + + @media (max-width: 768px) { + width: 100%; + + &:last-child { + margin-bottom: 48px; + } + } + } + + & > a { + display: flex; + cursor: pointer; + } + + & > a .course { + border: 1px solid var(--gray-6); + display: flex; + flex-direction: column; + width: 100%; + + & > div { + min-height: 200px; + padding: 25px; + } + + & .image { + background: var(--gray-7); + position: relative; + display: flex; + justify-content: center; + align-items: center; + + & img { + max-width: 80px; + max-height: 80px; + } + } + + & h4 { + color: var(--gray-2); + } + + & .time { + color: var(--gray-4); + position: absolute; + top: 10px; + right: 10px; + } + + & .content { + text-align: center; + background: #fff; + @media (max-width: 768px) { + display: flex; + justify-content: center; + align-items: center; + } + + & h4 { + margin: 24px 0px; + } + } + } + } + + &.brand-consul { + & .content label { + color: var(--consul); + } + } + + /* Brand -- Nomad */ + &.brand-nomad { + & .content label { + color: var(--nomad); + } + } + + /* Brand -- Packer */ + &.brand-packer { + & .content label { + color: var(--packer); + } + } + + /* Brand -- Terraform */ + &.brand-terraform { + & .content label { + color: var(--terraform); + } + } +} diff --git a/website/pages/home/index.jsx b/website/pages/home/index.jsx index d009bbc2aa..06f4539199 100644 --- a/website/pages/home/index.jsx +++ b/website/pages/home/index.jsx @@ -1,3 +1,125 @@ +import CallToAction from '@hashicorp/react-call-to-action' +import UseCases from '@hashicorp/react-use-cases' +import BasicHero from '../../components/basic-hero' +import ConsulEnterpriseComparison from '../../components/consul-enterprise-comparison' +import LearnCallout from '../../components/learn-callout' + export default function HomePage() { - return <> + return ( +
+ +
+ +
+ + + + +
+ ) } diff --git a/website/pages/home/style.css b/website/pages/home/style.css index 3f807df920..5b620fa580 100644 --- a/website/pages/home/style.css +++ b/website/pages/home/style.css @@ -1,449 +1,31 @@ -.large-padding { - padding: 120px 0; -} - -.small-padding { - padding: 56px; -} - -.bg-light { - background: #f7f8fa !important; -} - -#companies-using-consul h2 { - margin-bottom: 24px; -} - -#static-dynamic { - & .g-section-header { - margin-bottom: 64px; - } -} - -.g-use-cases { - margin: 48px 0 8px; - - @media (min-width: 768px) { - display: flex; - justify-content: center; - margin: 0 20px; +.p-home { + & > section { + padding-top: 100px; + padding-bottom: 100px; } - & .button { - background: #ca2171; - border-radius: 1px; - box-sizing: border-box; - color: #ffffff; - display: inline-block; - font-family: 'gilmer-web', 'Gilmer', Geneva, Tahoma, Helvetica, Verdana, - sans-serif; - font-size: 0.938rem; - font-weight: 500; - line-height: 1.6em; - margin-bottom: 4px; - padding: 14px 20px; - text-decoration: none; - } + & .use-cases { + padding-top: 128px; + padding-bottom: 64px; - & img { - width: 51%; - - @media (min-width: 768px) { - width: 80%; - } - } - - & h3 { - font-weight: 600; - margin: 40px 0 0; - - & span { - display: block; - font-weight: 300; - } - } - - & p { - margin-top: 0.5em; - - @media (min-width: 768px) { - margin-top: 1em; - } - } - - & > div { - padding: 0 16px; - position: relative; - text-align: center; - - @media (min-width: 768px) { - padding: 0 20px; - width: 33.33333%; + @media (max-width: 800px) { + padding-top: 88px; } - @media (min-width: 992px) { - display: flex; - flex-direction: column; - justify-content: space-between; - } - - & + div { - margin-top: 56px; - - @media (min-width: 768px) { - margin-top: 0; - } - } - } - - & .button { - @media (min-width: 768px) { - margin-top: 22px; - } - } -} - -.g-logo-grid { - align-items: center; - display: flex; - flex-wrap: wrap; - margin: 0 auto; - max-width: 990px; - text-align: center; - - & > div { - width: 33%; - - & img { - width: 100%; - } - - @media (max-width: 768px) { - width: 50%; - } - } -} - -.g-text-asset { - text-align: center; - - @media (min-width: 768px) { - align-content: space-between; - display: flex; - justify-content: center; - margin: 0 -20px; - text-align: left; - } - - &.reverse { - flex-direction: row-reverse; - - & > div:first-child > div { - margin-left: auto; - - @media (min-width: 768px) { - padding: 0 24px 0 0; - } - - @media (min-width: 992px) { - padding-right: 32px; - } - } - } - - &.large { - margin-bottom: -56px; - - @media (min-width: 768px) { - margin-bottom: -96px; - } - - @media (min-width: 992px) { - margin-bottom: -120px; - } - - & > div:last-child { - justify-content: unset; - } - - & picture > img, - & img { - width: 145%; - vertical-align: middle; - } - } - - & > div { - @media (min-width: 768px) { - align-items: center; - margin: 0 auto; - display: flex; - padding: 0 20px; - width: 50%; - } - - &:first-child { - margin-bottom: 32px; - - @media (min-width: 768px) { - margin-bottom: 0; - } - - & > div { - @media (min-width: 768px) { - margin-left: 0; - max-width: 454px; - padding: 0 0 0 24px; - } - - @media (min-width: 992px) { - padding-left: 32px; - } - - & a { - color: #1563ff; - - &:focus, - &:hover { - color: #2c72fe; - - & path { - fill: #2c72fe; - } - } - - & svg { - margin-left: 10px; - } - } + & .g-use-cases { + & .icon { + /* min-height: 140px; */ + margin-bottom: 25px; } } - &:last-child { - justify-content: center; - } - - & img, - & picture > img { - width: 100%; - - &.shadow { - box-shadow: 0 40px 48px -20px rgba(63, 68, 85, 0.4); - } - } - - & > svg { - max-width: 100%; - } - - &.code-sample > div { - box-shadow: 0 40px 48px -20px rgba(63, 68, 85, 0.4); - color: var(--white); + & h2 { margin: 0; - text-align: left; - width: 100%; - - & span { - background: #252937; - border-bottom: 1px solid rgba(255, 255, 255, 0.15); - display: block; - height: 32px; - - @media (min-width: 768px) { - height: 40px; - } - } - - & .code { - background: #1e212a; - overflow: auto; - padding: 16px 24px 32px; - width: 100%; - - & code { - background: #1e212a; - border-radius: 0; - color: var(--white); - white-space: pre; - - &.keyword { - color: #be5580; - } - } - } - } - - &.logos { - justify-content: center; - - & > div { - align-items: center; - display: flex; - flex-direction: column; - - & img, - picture { - margin: 30px 0; - - @media (max-width: 767px) { - margin: 15px 0; - max-width: 51%; - } - } - } - } - } - - & h3 { - margin: 0 0 16px 0; - - @media (min-width: 768px) { - margin-top: 16px; - } - } - - & i { - list-style-type: none; - margin: 16px; - text-align: left; - - @media (min-width: 768px) { - margin: 16px 0; - } - - &:before { - background-position: center; - background-repeat: no-repeat; - background-size: 100%; - background-image: url('data:image/svg+xml;utf8,'); - content: ''; - display: block; - float: left; - height: 24px; - margin: 4px 0 0 -40px; - width: 24px; - } - } -} - -#static-dynamic { - & .static-callout { - width: 85%; - } - - & #index-dynamic-animation { - width: 85%; - margin: 0 auto; - - & svg { - width: 100%; - height: auto !important; - } - } -} - -.home-cta-section { - color: var(--white); - - @media (min-width: 768px) { - display: flex; - } - - & > div { - display: flex; - justify-content: center; - padding: 64px 24px; - overflow: hidden; - - @media (min-width: 768px) { - padding-top: 80px; - padding-bottom: 80px; - width: 50%; - } - - @media (min-width: 992px) { - padding-top: 96px; - padding-bottom: 96px; - width: 50%; - } - - & > svg { - width: 135px; - } - - &:first-child { - background: var(--consul); - position: relative; - - &:after { - content: ''; - background: url('/img/consul-connect/mesh.svg') top center; - background-size: cover; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - opacity: 0.2; - transform: scale(1.3, 1.3); - } - } - - &:last-child { - background: var(--gray-1); - } - - & > div { - display: flex; - flex-direction: column; - max-width: 564px; text-align: center; - width: 100%; - z-index: 1; - - & div:first-child { - height: 50px; - width: auto; - } - - & p { - flex: 1 0 auto; - margin: 24px 0 32px; + margin-bottom: 64px; + @media (max-width: 800px) { + margin-bottom: 48px; } } } } - -.button { - background: #ca2171; - border-radius: 1px; - box-sizing: border-box; - color: #ffffff; - display: inline-block; - font-family: 'gilmer-web', 'Gilmer', Geneva, Tahoma, Helvetica, Verdana, - sans-serif; - font-size: 0.938rem; - font-weight: 500; - line-height: 1.6em; - margin-bottom: 4px; - padding: 14px 20px; - text-decoration: none; -} - -.button.white { - background: #ffffff; - color: #000000; -} - -.button.secondary.white { - border: 1px solid rgba(255, 255, 255, 0.24); - color: #ffffff; -} - -.button.secondary { - background: transparent; - border: 1px solid rgba(29, 30, 35, 0.2); - color: #000000; - padding: 13px 19px; -} -/* */ diff --git a/website/pages/style.css b/website/pages/style.css index 277d5537c6..c39b4276da 100644 --- a/website/pages/style.css +++ b/website/pages/style.css @@ -37,11 +37,15 @@ @import '~@hashicorp/react-tabs/dist/style.css'; @import '~@hashicorp/react-code-block/dist/style.css'; @import '~@hashicorp/react-alert-banner/dist/style.css'; +@import '~@hashicorp/react-use-cases/dist/style.css'; /* Local Components */ +@import '../components/basic-hero/style.css'; +@import '../components/enterprise-comparison/style.css'; @import '../components/footer/style.css'; @import '../components/before-after/style.css'; @import '../components/tabs/style.css'; +@import '../components/learn-callout/style.css'; /* Local Pages */ @import './downloads/style.css'; diff --git a/website/public/img/hero-pattern-mobile.svg b/website/public/img/hero-pattern-mobile.svg new file mode 100644 index 0000000000..b9c3153aad --- /dev/null +++ b/website/public/img/hero-pattern-mobile.svg @@ -0,0 +1,319 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/website/public/img/hero-pattern.svg b/website/public/img/hero-pattern.svg new file mode 100644 index 0000000000..372b243204 --- /dev/null +++ b/website/public/img/hero-pattern.svg @@ -0,0 +1,1688 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +